跳到主要内容

32篇带“engineering”标签的文章

查看所有标签

React Native 月报 #3

·阅读时长5分钟
Mike Grabowski
Mike Grabowski
CTO 兼 Callstack 联合创始人

React Native 月度会议继续进行!本月的会议时间有点短,因为我们的大部分团队都在忙着发货。下个月,我们将在波兰弗罗茨瓦夫参加React Native EU大会。务必抢到门票,届时现场见!同时,让我们看看我们的团队都在忙些什么。

团队

本次第三次会议,有 5 个团队加入了我们

备注

以下是各团队的备注

Callstack

  • 最近开源了react-native-material-palette。它从图像中提取主要颜色,帮助您创建视觉上引人入胜的应用程序。目前它只支持 Android,但我们正在考虑未来添加对 iOS 的支持。
  • 我们已将 HMR 支持和许多其他很酷的东西加入到 haul 中!查看最新版本。
  • React Native 欧洲大会 2017 即将到来!下个月将是 React Native 和波兰的天下!请务必在这里抢购最后几张票。

Expo

  • 发布了对在Snack上安装 npm 包的支持。通常的 Expo 限制仍然适用——包不能依赖于 Expo 中尚未包含的自定义原生 API。我们还在努力支持 Snack 中的多个文件和上传资产。Satyajit将在React Native Europe谈论 Snack。
  • 发布了 SDK20,支持相机、支付、安全存储、磁力计、文件系统下载的暂停/恢复,以及改进的启动/加载屏幕。
  • 继续与Krzysztof合作开发react-native-gesture-handler。请尝试一下,使用 PanResponder 或原生手势识别器重新构建您之前构建的一些手势,并让我们知道您遇到了哪些问题。
  • 正在试验 JSC 调试协议,并处理 Canny 上的一堆功能请求。

Facebook

  • 上个月我们讨论了 GitHub 问题跟踪器的管理,以及我们将尝试改进以解决项目的可维护性问题。
  • 目前,未解决的问题数量稳定在 600 左右,看来可能会保持一段时间。在过去的一个月里,我们因缺乏活动(定义为过去 60 天内没有评论)而关闭了 690 个问题。在这 690 个问题中,有 58 个因各种原因被重新打开(维护者承诺提供修复,或贡献者提出了很好的理由来保持问题打开)。
  • 我们计划在可预见的未来继续自动关闭过时问题。我们希望达到这样的状态:跟踪器中打开的每个有影响的问题都能得到处理,但我们还没有达到。我们需要维护者的所有帮助来分类问题,并确保我们不会遗漏引入回归或引入重大更改的问题,特别是那些影响新创建项目的问题。有兴趣提供帮助的人可以使用 Facebook GitHub Bot 来分类问题和拉取请求。新的维护者指南包含有关分类和使用 GitHub Bot 的更多信息。请将自己添加到问题工作组,并鼓励其他活跃社区成员也这样做!

Microsoft

  • 新的 Skype 应用基于 React Native 构建,以尽可能多地在平台之间共享代码。基于 React Native 的 Skype 应用目前可在 Android 和 iOS 应用商店中获取。
  • 在 React Native 上构建 Skype 应用程序时,我们会向 React Native 发送拉取请求,以解决我们遇到的错误和缺失功能。到目前为止,我们已经合并了大约70 个拉取请求
  • React Native 使我们能够从同一代码库为 Android 和 iOS Skype 应用程序提供支持。我们还希望使用该代码库为 Skype 网络应用程序提供支持。为了帮助我们实现这一目标,我们构建并开源了一个基于 React/React Native 的薄层,名为ReactXP。ReactXP 提供了一组跨平台组件,在针对 iOS/Android 时映射到 React Native,在针对 Web 时映射到 react-dom。ReactXP 的目标与另一个名为 React Native for Web 的开源库类似。ReactXP FAQ中简要描述了这些库的方法有何不同。

Shoutem

  • 我们正在继续努力改进和简化使用 Shoutem 构建应用程序时的开发人员体验。
  • 开始将我们所有的应用程序迁移到 react-navigation,但我们最终推迟了这一进程,直到更稳定的版本发布,或者其中一个原生导航解决方案变得稳定。
  • 将所有扩展和我们的大部分开源库(animationthemeui)更新到 React Native 0.47.1。

下一次会议

下一次会议定于 2017 年 9 月 13 日星期三举行。由于这只是我们的第三次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们如何改进会议成果有任何建议,请随时在 Twitter 上联系我

市场中的 React Native 性能

·6 分钟阅读
Facebook 软件工程师

React Native 在 Facebook 家族的多个应用程序中用于多个地方,包括主要 Facebook 应用程序中的顶级选项卡。本文的重点是一个高度可见的产品,Marketplace。它在十几个国家/地区可用,使用户能够发现其他用户提供的产品和服务。

在 2017 年上半年,通过 Relay 团队、Marketplace 团队、移动 JS 平台团队和 React Native 团队的共同努力,我们使 Android Year Class 2010-11 设备的 Marketplace 交互时间 (TTI) 缩短了一半。Facebook 历来将这些设备视为低端 Android 设备,它们在任何平台或设备类型上的 TTI 都最慢。

典型的 React Native 启动过程如下所示:

免责声明:比例不具代表性,会因 React Native 的配置和使用方式而异。

我们首先初始化 React Native 核心(即“Bridge”),然后运行特定于产品的 JavaScript,它决定了 React Native 将在原生处理时间中渲染哪些原生视图。

一种不同的方法

我们早期犯的一个错误是让Systrace 和 CTScan驱动我们的性能优化工作。这些工具在 2016 年帮助我们发现了许多唾手可得的成果,但我们发现 Systrace 和 CTScan 并不能代表生产场景,也无法模拟实际情况。时间细分中的时间花费比例通常不正确,有时甚至大相径庭。极端情况下,我们预期只需几毫秒的事情实际却需要数百或数千毫秒。尽管如此,CTScan 仍然有用,我们发现它能在回归进入生产环境之前捕获三分之一的回归。

在 Android 上,我们将这些工具的不足归因于以下事实:1) React Native 是一个多线程框架,2) Marketplace 与新闻源和其他顶级选项卡等大量复杂视图并置,以及 3) 计算时间差异很大。因此,本学期,我们让生产测量和细分驱动我们几乎所有的决策和优先级划分。

生产仪器化的路径

表面上,生产环境的性能分析似乎很简单,但实际上却是一个相当复杂的过程。它经历了多个迭代周期,每个周期持续 2-3 周;由于将提交发布到主分支、将应用程序推送到 Play 商店以及收集足够的生产样本以对我们的工作充满信心存在延迟。每个迭代周期都涉及发现我们的分析是否准确,它们是否具有正确的粒度级别,以及它们是否正确地加起来了整个时间跨度。我们无法依赖 alpha 和 beta 版本,因为它们不代表普通人群。本质上,我们非常细致地基于数百万个样本的聚合构建了一个非常准确的生产跟踪。

我们之所以一丝不苟地验证细分中的每一毫秒是否都正确地累加到其父指标,原因之一是我们很早就意识到我们的仪器化存在空白。事实证明,我们最初的细分并未考虑线程跳转造成的停顿。线程跳转本身并不昂贵,但跳转到已经忙于工作的繁忙线程则非常昂贵。我们最终通过在正确的时间点散布 `Thread.sleep()` 调用,在本地重现了这些阻塞,并通过以下方式成功修复了它们:

  1. 移除对 AsyncTask 的依赖,
  2. 撤销在 UI 线程上强制初始化 ReactContext 和 NativeModules,以及
  3. 消除初始化时测量 ReactRootView 的依赖。

综合来看,消除这些线程阻塞问题将启动时间缩短了25%以上。

生产指标也挑战了我们之前的一些假设。例如,我们过去在启动路径上预加载许多 JavaScript 模块,假设将模块放在一个包中会降低它们的初始化成本。然而,预加载和共置这些模块的成本远远超过了收益。通过重新配置我们的内联 require 黑名单并从启动路径中移除 JavaScript 模块,我们能够避免加载不必要的模块,例如 Relay Classic(当只需要Relay Modern时)。今天,我们的 RUN_JS_BUNDLE 分解速度提高了 75% 以上。

我们还通过调查产品特定的原生模块获得了成功。例如,通过惰性注入原生模块的依赖项,我们将该原生模块的成本降低了 98%。通过消除 Marketplace 启动与其他产品之间的竞争,我们将启动时间缩短了等效的时间间隔。

最棒的是,其中许多改进都广泛适用于所有使用 React Native 构建的屏幕。

结论

人们认为 React Native 启动性能问题是由于 JavaScript 慢或网络时间过长造成的。虽然加快 JavaScript 等会使 TTI 显着降低,但这些因素对 TTI 的贡献百分比远低于之前的估计。

到目前为止的教训是**衡量,衡量,再衡量!**一些胜利来自于将运行时成本转移到构建时,例如 Relay Modern 和惰性原生模块。另一些胜利来自于通过更聪明地并行化代码或移除无用代码来避免工作。还有一些胜利来自于 React Native 的大型架构更改,例如清理线程阻塞。性能没有万能的解决方案,长期的性能提升将来自于渐进式的性能分析和改进。不要让认知偏见影响你的决策。相反,仔细收集和解释生产数据以指导未来的工作。

未来计划

从长远来看,我们希望 Marketplace 的 TTI 能够与使用原生技术构建的类似产品相媲美,并且通常情况下,React Native 的性能能够与原生性能持平。此外,尽管本学期我们将桥接启动成本大幅降低了约 80%,但我们计划通过 Prepack 和更多的构建时处理等项目,将 React Native 桥接的成本降至接近于零。

React Native 月报 #2

·阅读时长 9 分钟
Tomislav Tenodi
Shoutem 产品经理

React Native 月度会议继续进行!本次会议,我们有幸邀请到Infinite Red,他们是Chain React,React Native 大会的幕后主脑。由于这里的大多数人都将在 Chain React 上发表演讲,我们将会议推迟了一周。大会的演讲已经在线发布,我鼓励大家去看看。那么,让我们看看我们的团队都在忙些什么。

团队

第二次会议,共有9个团队加入我们

备注

以下是各团队的备注

Airbnb

Callstack

  • Mike Grabowski一如既往地管理着 React Native 的每月发布,包括一些已推出的测试版。特别是,他正在努力将 v0.43.5 版本发布到 npm,因为它解除了 Windows 用户的限制!
  • Haul 的工作虽然缓慢但持续进行中。有一个拉取请求添加了 HMR,并且已经发布了其他改进。最近有几位行业领导者采用了它。可能计划在这一领域开始全职有偿工作。
  • 来自 Jest 团队的 Michał Pierzchała 本月加入了我们 Callstack。他将协助维护 Haul,并可能参与 Metro BundlerJest 的工作。
  • Satyajit Sahoo 现在和我们在一起了,太棒了!
  • 我们的开源部门即将推出一系列酷炫的东西。特别是,正在努力将 Material Palette API 引入 React Native。计划最终发布我们的原生 iOS 套件,旨在提供 1:1 的原生组件外观和感觉。

Expo

  • 最近推出了Native Directory,以帮助在 React Native 生态系统中发现和评估库。问题在于:库太多,难以测试,需要手动应用启发式方法,并且不立即明显哪些是您应该使用的最佳库。也很难知道某些东西是否与 CRNA/Expo 兼容。因此 Native Directory 试图解决这些问题。查看它并将您的库添加到其中。库列表在这里。这只是我们的第一次尝试,我们希望它由社区拥有和运营,而不仅仅是 Expo 团队。因此,如果您认为这有价值并希望使其更好,请加入我们!
  • 使用 Expo SDK 19 在Snack中添加了对安装 npm 包的初始支持。如果您遇到任何问题,请告诉我们,我们仍在解决一些错误。与 Native Directory 一起,这应该可以轻松测试仅具有 JS 依赖项或Expo SDK中包含的依赖项的库。试一试
  • 发布了 Expo SDK19,其中包含全面的改进,我们现在正在使用更新的 Android JSC
  • 正在与 Alexander Kotliarskyi 合作撰写文档指南,其中包含有关如何改善应用用户体验的提示列表。请加入并添加到列表中或帮助编写其中一部分!
  • 继续致力于:音频/视频、相机、手势(与 Software Mansion 合作,`react-native-gesture-handler`)、GL 相机集成,并希望在 SDK20(8 月)中首次实现其中一些功能,届时其他功能也将得到显著改进。我们才刚刚开始为 Expo 客户端构建后台工作基础设施(地理定位、音频、处理通知等)。
  • Adam Miskiewiczreact-navigation中模仿UINavigationController的过渡方面取得了一些不错的进展。查看他推文中的早期版本 - 很快将发布。还要查看他上游MaskedViewIOS。如果您有能力并希望为 Android 实现 MaskedView,那将非常棒!

Facebook

  • Facebook 正在内部探索将原生 ComponentKitLitho 组件嵌入到 React Native 中。
  • 非常欢迎对 React Native 的贡献!如果您想知道如何贡献,“如何贡献”指南描述了我们的开发过程并列出了发送您的第一个拉取请求的步骤。还有其他不需要编写代码的贡献方式,例如通过分类问题或更新文档。
    • 截至撰写本文时,React Native 拥有 **635** 未解决问题和 **249** 未解决的拉取请求。这让我们的维护人员不堪重负,当内部修复问题时,很难确保相关任务得到更新。
    • 我们不确定在保持社区满意度的同时处理此问题的最佳方法是什么。一些(但不是全部!)选项包括关闭过时问题、给予更多人管理问题的权限,以及自动关闭不遵循问题模板的问题。我们撰写了“对维护者的期望”指南,以设定期望并避免意外。如果您对我们如何改进维护者体验并确保提出问题和拉取请求的人员感到被倾听和被重视有任何想法,请告诉我们!

GeekyAnts

  • 我们在 Chain React 上演示了与 React Native 文件协同工作的设计工具。许多与会者报名了候补名单。
  • 我们还在研究其他跨平台解决方案,例如Google Flutter(即将进行重大比较)、Kotlin NativeApache Weex,以了解架构差异以及我们可以从中学到什么来提高 React Native 的整体性能。
  • 我们的大部分应用程序都切换到了 react-navigation,这改善了整体性能。
  • 此外,还发布了 NativeBase Market - 一个面向(并由)开发者的 React Native 组件和应用程序市场。

Infinite Red

Microsoft

  • CodePush现已集成到移动中心。现有用户的工作流程将保持不变。
    • 一些人报告了重复应用程序的问题——他们已经在 Mobile Center 上有一个应用程序。我们正在努力解决这些问题,但是如果您有两个应用程序,请告诉我们,我们可以为您合并它们。
  • Mobile Center 现在支持 CodePush 的推送通知。我们还展示了如何将通知和 CodePush 结合起来用于 A/B 测试应用程序——这是 React Native 架构独有的功能。
  • VS Code 在 ReactNative 上有一个已知的调试问题——扩展的下一个版本将在几天内修复此问题。
  • 由于微软内部还有许多其他团队也在从事 React Native 的工作,我们将在下次会议上努力争取所有团队的更好代表。

Shoutem

  • 完成了使Shoutem上的 React Native 开发更容易的流程。在 Shoutem 上开发应用程序时,您可以使用所有标准的 react-native 命令。
  • 我们做了大量工作,试图找出如何最好地在 React Native 上进行性能分析。很大一部分文档已经过时,我们将尽力在官方文档上创建一个拉取请求,或者至少在博客文章中写下我们的一些结论。
  • 我们正在将导航解决方案切换到 react-navigation,因此我们可能很快会有一些反馈。
  • 我们在工具包中发布了一个新的 HTML 组件,它将原始 HTML 转换为 React Native 组件树。

Wix

  • 我们开始着手向Metro Bundler提交一个拉取请求,其中包含react-native-repackager的功能。我们更新了 react-native-repackager 以支持 RN 44(我们在生产环境中使用)。我们正在将其用于detox的模拟基础设施。
  • 在过去三周中,我们一直在用 detox 测试 Wix 应用程序。这是一个令人惊叹的学习经验,了解如何在此规模(超过 40 名工程师)的应用程序中减少手动 QA。因此,我们解决了 detox 的几个问题,新版本刚刚发布。我很高兴地报告,我们正在践行“零缺陷政策”,到目前为止测试一直稳定通过。
  • Detox for Android 进展顺利。我们得到了社区的大力帮助。我们预计在大约两周内推出初始版本。
  • DetoxInstruments,我们的性能测试工具,正在变得比我们最初计划的更大。我们现在计划将其转换为一个独立工具,它将不会与 detox 紧密耦合。它将允许全面调查 iOS 应用程序的性能。它还将与 detox 集成,因此我们可以在性能指标上运行自动化测试。

下一次会议

下一次会议定于 2017 年 8 月 16 日举行。由于这只是我们的第二次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们如何改进会议成果有任何建议,请随时在 Twitter 上联系我

React Native 月报 #1

·6 分钟阅读
Tomislav Tenodi
Shoutem 产品经理

Shoutem,我们很幸运能从一开始就与 React Native 合作。我们决定从第一天起就成为这个惊人社区的一部分。很快,我们意识到要跟上社区成长和改进的步伐几乎是不可能的。这就是为什么我们决定组织一个每月例会,所有主要的 React Native 贡献者都可以在会上简要介绍他们的努力和计划。

每月例会

我们于 2017 年 6 月 14 日举行了月度会议的第一次会议。React Native 月度会议的使命简单明了:**改善 React Native 社区**。展示团队的努力可以促进团队之间的线下协作。

团队

在第一次会议上,有8个团队加入了我们

我们希望有更多的核心贡献者加入接下来的会议!

备注

由于团队的计划可能会引起更广泛受众的兴趣,我们将在这里,在 React Native 博客上分享它们。所以,它们来了

Airbnb

  • 计划为 ViewAccessibilityInfo 原生模块添加一些 A11y(可访问性)API。
  • 将调查在 Android 上为原生模块添加一些 API,以允许指定它们运行的线程。
  • 一直在调查潜在的初始化性能改进。
  • 一直在调查一些更复杂的打包策略,以便在“unbundle”之上使用。

Callstack

  • 正在研究通过使用 Detox 进行 E2E 测试来改进发布流程。拉取请求应该很快就会合并。
  • 他们一直在开发的 Blob 拉取请求已合并,后续拉取请求即将到来。
  • 提高Haul在内部项目中的采用率,以了解其与Metro Bundler相比的性能。与 webpack 团队合作,提高多线程性能。
  • 在内部,他们已经建立了一个更好的基础设施来管理开源项目。计划在未来几周内推出更多内容。
  • React Native Europe 会议即将举行,目前还没有特别有趣的内容,但欢迎大家参加!
  • 暂时退出了 react-navigation 一段时间,以调查替代方案(特别是原生导航)。

Expo

Facebook

  • React Native 的打包器现在是Metro Bundler,位于一个独立的仓库中。伦敦的 Metro Bundler 团队很高兴能满足社区的需求,提高模块化以支持 React Native 之外的更多用例,并提高对问题和 PR 的响应速度。
  • 在接下来的几个月里,React Native 团队将致力于完善原始组件的 API。预计在布局怪癖、可访问性和流程类型方面会有改进。
  • React Native 团队还计划今年通过重构来改进核心模块化,以完全支持 Windows 和 macOS 等第三方平台。

GeekyAnts

  • 团队正在开发一款 UI/UX 设计应用程序(代号:Builder),它直接处理 .js 文件。目前,它只支持 React Native。它类似于 Adobe XD 和 Sketch。
  • 团队正在努力工作,以便您可以在编辑器中加载现有的 React Native 应用程序,进行更改(以设计师的视角进行视觉更改),并将更改直接保存到 JS 文件中。
  • 大家正在努力弥合设计师和开发者之间的差距,并将他们带到同一个代码仓库中。
  • 此外,NativeBase 最近达到了 5,000 个 GitHub 星。

Microsoft

  • CodePush现已集成到移动中心。这是提供更集成的分发、分析和其他服务体验的第一步。请参阅他们的公告此处
  • VS Code 在调试方面存在一个 bug,他们正在努力修复,并将发布新的构建版本。
  • 正在研究 Detox 进行集成测试,并研究 JSC Context 以获取变量和崩溃报告。

Shoutem

  • 通过 React Native 社区的工具,让 Shoutem 应用的开发变得更容易。您将能够使用所有 React Native 命令来运行在Shoutem上创建的应用程序。
  • 正在研究 React Native 的性能分析工具。他们在设置时遇到了很多问题,并将撰写他们在此过程中发现的一些见解。
  • Shoutem 正在努力使 React Native 与现有原生应用程序的集成变得更容易。他们将记录公司内部开发的概念,以获取社区的反馈。

Wix

  • 正在内部努力采用Detox,以将 Wix 应用程序的大部分内容转移到“零手动 QA”。因此,Detox 正在生产环境中被数十名开发人员大量使用,并迅速成熟。
  • 正在努力为Metro Bundler添加支持,以便在构建期间覆盖任何文件扩展名。除了“ios”和“android”,它还将支持任何自定义扩展名,如“e2e”或“detox”。计划将其用于 E2E 模拟。已经有一个名为react-native-repackager的库,现在正在处理一个 PR。
  • 正在研究性能测试自动化。这是一个名为 DetoxInstruments 的新仓库。您可以查看一下,它正在开源开发中。
  • 与 KPN 的一位贡献者合作,开发适用于 Android 的 Detox 并支持真实设备。
  • 正在考虑将“Detox 作为一个平台”来构建其他需要自动化模拟器/设备的工具。一个例子是 React Native 的Storybook或 Ram 的集成测试想法。

下一次会议

会议将每四周举行一次。下一次会议定于 2017 年 7 月 12 日举行。由于我们刚开始召开此次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们下次会议应该涵盖什么内容,或者我们应该如何改进会议成果有任何建议,请随时在 Twitter 上联系我

React Native 中更好的列表视图

·6 分钟阅读

在我们在社区群组中发布预告公告后,许多人已经开始使用我们一些新的列表组件,但我们今天正式宣布它们!不再有 `ListView` 或 `DataSource`、过时行、被忽略的错误或过度的内存消耗——通过最新的 React Native 2017 年 3 月发布候选版本(`0.43-rc.1`),您可以从新的组件套件中选择最适合您用例的组件,开箱即用,具有出色的性能和功能集

<FlatList>

这是用于简单、高性能列表的主力组件。提供一个数据数组和一个renderItem函数,就可以开始使用了。

<FlatList
data={[{title: 'Title Text', key: 'item1'}, ...]}
renderItem={({item}) => <ListItem title={item.title} />}
/>

<SectionList>

如果你想渲染一组按逻辑部分划分的数据,可能带有部分标题(例如,在按字母顺序排列的地址簿中),并且可能带有异构数据和渲染(例如,一个个人资料视图,带有一些按钮,然后是编写器,然后是照片网格,然后是朋友网格,最后是故事列表),那么这就是你的最佳选择。

<SectionList
renderItem={({item}) => <ListItem title={item.title} />}
renderSectionHeader={({section}) => <H1 title={section.key} />}
sections={[ // homogeneous rendering between sections
{data: [...], key: ...},
{data: [...], key: ...},
{data: [...], key: ...},
]}
/>

<SectionList
sections={[ // heterogeneous rendering between sections
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
]}
/>

<VirtualizedList>

更灵活的 API 的幕后实现。如果您的数据不是纯数组(例如,一个不可变列表),则特别方便。

功能

列表在许多情况下使用,因此我们为新组件提供了许多功能,以处理大多数开箱即用的用例。

  • 滚动加载 (onEndReached)。
  • 下拉刷新 (onRefresh / refreshing)。
  • 可配置可见性 (VPV) 回调 (onViewableItemsChanged / viewabilityConfig)。
  • 水平模式 (horizontal)。
  • 智能项目和节分隔符。
  • 多列支持 (numColumns)
  • scrollToEndscrollToIndexscrollToItem
  • 更好的 Flow 类型。

一些注意事项

  • 当内容滚动出渲染窗口时,项目子树的内部状态不会保留。确保所有数据都捕获在项目数据或外部存储中,如 Flux、Redux 或 Relay。

  • 这些组件基于 `PureComponent`,这意味着如果 `props` 保持浅层相等,它们将不会重新渲染。请确保 `renderItem` 函数直接依赖的所有内容都作为更新后不 `===` 的 prop 传递,否则您的 UI 可能无法随更改而更新。这包括 `data` prop 和父组件状态。例如

    <FlatList
    data={this.state.data}
    renderItem={({item}) => (
    <MyItem
    item={item}
    onPress={() =>
    this.setState(oldState => ({
    selected: {
    // New instance breaks `===`
    ...oldState.selected, // copy old data
    [item.key]: !oldState.selected[item.key], // toggle
    },
    }))
    }
    selected={
    !!this.state.selected[item.key] // renderItem depends on state
    }
    />
    )}
    selected={
    // Can be any prop that doesn't collide with existing props
    this.state.selected // A change to selected should re-render FlatList
    }
    />
  • 为了限制内存并实现平滑滚动,内容在屏幕外异步渲染。这意味着滚动速度可能快于填充率,并暂时看到空白内容。这是一个可以在权衡后调整以适应每个应用程序需求的功能,我们正在幕后努力改进它。

  • 默认情况下,这些新列表会在每个项目上查找一个key prop,并将其用作 React 键。或者,您可以提供一个自定义的keyExtractor prop。

性能

除了简化 API,新的列表组件还具有显著的性能增强,其中最主要的是无论行数多少,内存使用量几乎恒定。这是通过“虚拟化”渲染窗口之外的元素来实现的,即将其从组件层次结构中完全卸载,并回收 React 组件的 JS 内存以及阴影树和 UI 视图的本地内存。这有一个注意事项,即内部组件状态将不保留,因此**请确保将任何重要状态跟踪在组件本身之外,例如在 Relay 或 Redux 或 Flux 存储中。**

限制渲染窗口还减少了 React 和原生平台需要完成的工作量,例如视图遍历。即使您正在渲染一百万个元素中的最后一个,使用这些新列表也不需要遍历所有这些元素才能渲染。您甚至可以使用 scrollToIndex 跳到中间,而无需过多的渲染。

我们还在调度方面做出了一些改进,这应该有助于提高应用程序的响应能力。渲染窗口边缘的项目渲染频率较低,优先级较低,在任何活动手势、动画或其他交互完成后才进行渲染。

高级用法

与 `ListView` 不同,渲染窗口中的所有项目在任何 props 更改时都会重新渲染。通常这没有问题,因为窗口化将项目数量减少到恒定数量,但如果您的项目比较复杂,您应该确保遵循 React 性能最佳实践,并在您的组件中酌情使用 `React.PureComponent` 和/或 `shouldComponentUpdate` 以限制递归子树的重新渲染。

如果您可以在不渲染行的情况下计算其高度,则可以通过提供 `getItemLayout` 属性来改善用户体验。这使得使用例如 `scrollToIndex` 滚动到特定项目更加流畅,并且会改善滚动指示器 UI,因为可以在不渲染内容的情况下确定内容的高度。

如果您有替代数据类型,例如不可变列表,则<VirtualizedList>是最佳选择。它接受一个getItem prop,允许您返回任何给定索引的项目数据,并且具有更宽松的 Flow 类型。

如果您有不寻常的用例,还可以调整许多参数。例如,您可以使用 windowSize 来权衡内存使用和用户体验,使用 maxToRenderPerBatch 来调整填充率和响应速度,使用 onEndReachedThreshold 来控制何时发生滚动加载,等等。

未来工作

  • 现有界面的迁移(最终废弃ListView)。
  • 根据我们的需求/意见增加更多功能(请告诉我们!)。
  • 粘性节标题支持。
  • 更多性能优化。
  • 支持带状态的功能项目组件。

idx:存在函数

·阅读时长2分钟
Timothy Yung
Facebook 工程经理

在 Facebook,我们经常需要访问从 GraphQL 获取的数据结构中深度嵌套的值。在访问这些深度嵌套的值时,一个或多个中间字段常常是可空的。这些中间字段可能是空的原因有很多,从隐私检查失败到空只是表示非致命错误的最灵活方式。

不幸的是,访问这些深度嵌套的值目前既繁琐又冗长。

props.user &&
props.user.friends &&
props.user.friends[0] &&
props.user.friends[0].friends;

有一个ECMAScript 提案旨在引入存在运算符,这将使这更加方便。但在该提案最终确定之前,我们希望找到一种解决方案,可以提高我们的生活质量,保持现有的语言语义,并鼓励使用 Flow 进行类型安全。

我们想出了一个名为 idx 的存在性函数

idx(props, _ => _.user.friends[0].friends);

此代码片段中的调用与上面代码片段中的布尔表达式的行为类似,但重复性大大降低。idx 函数严格接受两个参数

  • 任何值,通常是您要访问嵌套值的对象或数组。
  • 一个函数,它接收第一个参数并访问其上的嵌套值。

理论上,`idx` 函数将尝试捕获访问空或 undefined 属性时发生的错误。如果捕获到此类错误,它将返回 null 或 undefined。(您可以在idx.js中看到这可能如何实现。)

在实践中,每次访问嵌套属性都进行 try-catch 是缓慢的,并且区分特定类型的 TypeError 是脆弱的。为了解决这些缺点,我们创建了一个 Babel 插件,将上述 `idx` 调用转换为以下表达式

props.user == null
? props.user
: props.user.friends == null
? props.user.friends
: props.user.friends[0] == null
? props.user.friends[0]
: props.user.friends[0].friends;

最后,我们为 idx 添加了一个自定义的 Flow 类型声明,允许在第二个参数中进行遍历的类型检查,同时允许对可空属性进行嵌套访问。

该函数、Babel 插件和 Flow 声明现已在GitHub 上提供。它们通过安装 **idx** 和 **babel-plugin-idx** npm 包,并将“idx”添加到 `.babelrc` 文件中的插件列表来使用。

引入 Create React Native App

·3 分钟阅读
Adam Perry
Expo 软件工程师

今天,我们宣布Create React Native App:一个新工具,它大大简化了 React Native 项目的入门!它深受Create React App设计的启发,是FacebookExpo(原名 Exponent)合作的产物。

许多开发人员在安装和配置 React Native 当前的原生构建依赖项时遇到了困难,尤其是对于 Android。使用 Create React Native App,无需使用 Xcode 或 Android Studio,您可以使用 Linux 或 Windows 开发 iOS 设备。这是通过 Expo 应用程序实现的,它加载并运行用纯 JavaScript 编写的 CRNA 项目,无需编译任何原生代码。

尝试创建一个新项目(如果已安装 yarn,请替换为合适的 yarn 命令)

$ npm i -g create-react-native-app
$ create-react-native-app my-project
$ cd my-project
$ npm start

这将启动 React Native 打包器并打印一个二维码。在Expo 应用程序中打开它以加载您的 JavaScript。对 `console.log` 的调用将转发到您的终端。您可以使用任何标准 React Native API 以及 Expo SDK

原生代码怎么办?

许多 React Native 项目都有需要编译的 Java 或 Objective-C/Swift 依赖项。Expo 应用程序确实包含用于相机、视频、联系人等的 API,并捆绑了流行的库,例如Airbnb 的 react-native-mapsFacebook 身份验证。但是,如果您需要 Expo 不捆绑的原生代码依赖项,那么您可能需要为其拥有自己的构建配置。就像 Create React App 一样,CRNA 支持“弹出”。

您可以运行 `npm run eject` 来获得一个与 `react-native init` 生成的项目非常相似的项目。届时,您将需要 Xcode 和/或 Android Studio,就像您从 `react-native init` 开始一样,添加库时使用 `react-native link` 将起作用,并且您将完全控制原生代码编译过程。

问题?反馈?

Create React Native App 现在已足够稳定,可供一般使用,这意味着我们非常渴望听到您使用它的体验!您可以在Twitter上找到我,或在GitHub 仓库上打开一个问题。非常欢迎拉取请求!

使用原生驱动动画

·6 分钟阅读
Janic Duplessis
App & Flow 软件工程师

在过去的一年里,我们一直致力于提高使用 Animated 库的动画性能。动画对于创造出色的用户体验至关重要,但也可能难以做得好。我们希望让开发人员能够轻松创建高性能动画,而不必担心某些代码会导致卡顿。

这是什么?

Animated API 的设计考虑了一个非常重要的约束,它是可序列化的。这意味着我们可以在动画开始之前将动画的所有信息发送到原生端,并允许原生代码在 UI 线程上执行动画,而无需在每一帧都通过桥接。这非常有用,因为一旦动画开始,JS 线程可能会被阻塞,但动画仍将流畅运行。在实践中,这种情况经常发生,因为用户代码在 JS 线程上运行,并且 React 渲染也可能长时间锁定 JS。

一些历史...

这个项目大约一年前开始,当时 Expo 在 Android 上构建了 li.st 应用。Krzysztof Magiera 受雇在 Android 上构建初始实现。结果运行良好,li.st 是第一个使用 Animated 实现原生驱动动画的应用。几个月后,Brandon Withrow 在 iOS 上构建了初始实现。之后,Ryan Gomba 和我致力于添加缺失的功能,例如对 Animated.event 的支持,以及修复我们在生产应用中使用时发现的错误。这确实是一项社区努力,我要感谢所有参与者以及 Expo 赞助了大部分开发。它现在被 React Native 中的 Touchable 组件以及新发布的 React Navigation 库中的导航动画所使用。

它是如何工作的?

首先,让我们看看目前使用 JS 驱动的 Animated 动画是如何工作的。使用 Animated 时,您声明一个代表您要执行的动画的节点图,然后使用驱动程序使用预定义的曲线更新 Animated 值。您还可以通过使用 `Animated.event` 将 Animated 值连接到 `View` 的事件来更新它。

以下是动画步骤及其发生位置的分解

  • JS:动画驱动器使用 requestAnimationFrame 在每一帧执行,并根据动画曲线计算出的新值更新它驱动的值。
  • JS:计算中间值并将其传递给附加到 View 的 props 节点。
  • JS:使用 setNativeProps 更新 View
  • JS 到原生桥。
  • 原生:更新 UIViewandroid.View

如您所见,大部分工作都在 JS 线程上进行。如果它被阻塞,动画将跳帧。它还需要在每一帧通过 JS 到原生桥来更新原生视图。

原生驱动程序所做的就是将所有这些步骤移到原生。由于 Animated 会生成动画节点的图形,因此可以在动画开始时一次性将其序列化并发送到原生,从而无需回调到 JS 线程;原生代码可以直接在 UI 线程上逐帧更新视图。

以下是我们如何序列化动画值和插值节点的示例(不是确切的实现,只是一个示例)。

创建原生值节点,这是将要动画化的值

NativeAnimatedModule.createNode({
id: 1,
type: 'value',
initialValue: 0,
});

创建原生插值节点,这告诉原生驱动程序如何插值一个值

NativeAnimatedModule.createNode({
id: 2,
type: 'interpolation',
inputRange: [0, 10],
outputRange: [10, 0],
extrapolate: 'clamp',
});

创建原生 props 节点,这告诉原生驱动程序它附加到视图的哪个 props

NativeAnimatedModule.createNode({
id: 3,
type: 'props',
properties: ['style.opacity'],
});

连接节点

NativeAnimatedModule.connectNodes(1, 2);
NativeAnimatedModule.connectNodes(2, 3);

将 props 节点连接到视图

NativeAnimatedModule.connectToView(3, ReactNative.findNodeHandle(viewRef));

这样,原生动画模块就拥有了更新原生视图所需的所有信息,而无需转到 JS 计算任何值。

剩下要做的就是通过指定我们想要的动画曲线类型和要更新的动画值来实际启动动画。计时动画也可以通过提前在 JS 中计算动画的每一帧来简化,以使原生实现更小。

NativeAnimatedModule.startAnimation({
type: 'timing',
frames: [0, 0.1, 0.2, 0.4, 0.65, ...],
animatedValueId: 1,
});

现在是动画运行时发生的事情的分解

  • 原生:原生动画驱动程序使用 CADisplayLinkandroid.view.Choreographer 在每一帧执行,并根据动画曲线计算出的新值更新它驱动的值。
  • 原生:计算中间值并将其传递给附加到原生视图的 props 节点。
  • 原生:更新 UIViewandroid.View

如您所见,不再有 JS 线程和桥接,这意味着更快的动画!🎉🎉

我如何在我的应用程序中使用它?

对于普通动画,答案很简单,只需在启动动画时在动画配置中添加 useNativeDriver: true 即可。

之前

Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
}).start();

之后

Animated.timing(this.state.animatedValue, {
toValue: 1,
duration: 500,
useNativeDriver: true, // <-- Add this
}).start();

动画值只与一个驱动程序兼容,因此如果您在某个值上启动动画时使用原生驱动程序,请确保该值上的每个动画也使用原生驱动程序。

它也适用于 `Animated.event`,如果您有一个必须遵循滚动位置的动画,这非常有用,因为如果没有原生驱动程序,由于 React Native 的异步性质,它总是会比手势慢一帧。

之前

<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }]
)}
>
{content}
</ScrollView>

之后

<Animated.ScrollView // <-- Use the Animated ScrollView wrapper
scrollEventThrottle={1} // <-- Use 1 here to make sure no events are ever missed
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
{ useNativeDriver: true } // <-- Add this
)}
>
{content}
</Animated.ScrollView>

注意事项

并非所有您可以使用 Animated 实现的功能目前都受 Native Animated 支持。主要的限制是您只能对非布局属性进行动画处理,例如 `transform` 和 `opacity` 将起作用,但 Flexbox 和位置属性将不起作用。另一个限制是 `Animated.event`,它只适用于直接事件而不适用于冒泡事件。这意味着它不适用于 `PanResponder`,但适用于 `ScrollView#onScroll` 等。

Native Animated 也已成为 React Native 的一部分很长时间了,但由于被认为是实验性的,从未被记录。因此,如果您想使用此功能,请确保您使用的是最新版本 (0.40+) 的 React Native。

资源

有关动画的更多信息,我建议观看Christopher Chedeau 的这个演讲

如果您想深入了解动画以及如何将其卸载到原生以改善用户体验,还可以观看Krzysztof Magiera 的这个演讲

React Native 应用的从右到左布局支持

·阅读约8分钟
Mengjue (Mandy) Wang
Facebook 软件工程师实习生

将应用发布到应用商店后,国际化是进一步扩大受众范围的下一步。全球有 20 多个国家和许多人使用从右到左 (RTL) 语言。因此,让您的应用支持 RTL 对他们来说是必要的。

我们很高兴地宣布,React Native 已经改进以支持 RTL 布局。这现在可以在react-native的 master 分支中获得,并将包含在下一个 RC 中:`v0.33.0-rc`

这涉及更改 RN 使用的核心布局引擎 css-layout、RN 核心实现以及特定的开源 JS 组件以支持 RTL。

为了在生产环境中对 RTL 支持进行实战测试,最新版本的 **Facebook 广告管理工具**应用程序(第一个跨平台 100% RN 应用程序)现在提供阿拉伯语和希伯来语版本,并支持 iOS 和 Android 的 RTL 布局。iOSAndroid。以下是它在这些 RTL 语言中的样子:

RN 对 RTL 支持的总体变更

css-layout已经有了布局中`start`和`end`的概念。在从左到右 (LTR) 布局中,`start`表示`left`,`end`表示`right`。但在 RTL 中,`start`表示`right`,`end`表示`left`。这意味着我们可以让 RN 依赖于`start`和`end`的计算来计算正确的布局,这包括`position`、`padding`和`margin`。

此外,css-layout已经使每个组件的方向继承自其父组件。这意味着,我们只需将根组件的方向设置为 RTL,整个应用程序就会翻转。

下图描述了高层次的变化:

这包括:

通过此更新,当您允许您的应用程序使用 RTL 布局时:

  • 每个组件布局将水平翻转
  • 如果您使用支持 RTL 的开源组件,某些手势和动画将自动具有 RTL 布局
  • 可能需要最少的额外努力才能使您的应用程序完全支持 RTL

让应用程序支持 RTL

  1. 为了支持 RTL,您应该首先将 RTL 语言包添加到您的应用程序中。

  2. 通过在原生代码开头调用 allowRTL() 函数来允许您的应用程序使用 RTL 布局。我们提供了此实用程序,以便仅在您的应用程序准备就绪时才应用 RTL 布局。这是一个示例:

    iOS

    // in AppDelegate.m
    [[RCTI18nUtil sharedInstance] allowRTL:YES];

    Android

    // in MainActivity.java
    I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
    sharedI18nUtilInstance.allowRTL(context, true);
  3. 对于 Android,您需要将 `android:supportsRtl="true"` 添加到 `AndroidManifest.xml` 文件中的 `` 元素中。

现在,当您重新编译应用程序并将设备语言更改为 RTL 语言(例如阿拉伯语或希伯来语)时,您的应用程序布局应该会自动更改为 RTL。

编写支持 RTL 的组件

通常,大多数组件已经支持 RTL,例如:

  • 从左到右布局
  • 从右到左布局

但是,有几种情况需要注意,您需要使用`I18nManager`。在`I18nManager`中,有一个常量`isRTL`来判断应用程序的布局是否为 RTL,以便您可以根据布局进行必要的更改。

具有方向含义的图标

如果您的组件有图标或图像,它们在 LTR 和 RTL 布局中的显示方式将相同,因为 RN 不会翻转您的源图像。因此,您应该根据布局样式翻转它们。

  • 从左到右布局
  • 从右到左布局

以下是根据方向翻转图标的两种方法:

  • 给图像组件添加 transform 样式

    <Image
    source={...}
    style={{transform: [{scaleX: I18nManager.isRTL ? -1 : 1}]}}
    />
  • 或者,根据方向改变图像源

    let imageSource = require('./back.png');
    if (I18nManager.isRTL) {
    imageSource = require('./forward.png');
    }
    return <Image source={imageSource} />;

手势和动画

在 Android 和 iOS 开发中,当您切换到 RTL 布局时,手势和动画与 LTR 布局相反。目前,在 RN 中,手势和动画不支持 RN 核心代码级别,而是组件级别。好消息是,其中一些组件今天已经支持 RTL,例如 `SwipeableRow``NavigationExperimental`。但是,其他带有手势的组件需要手动支持 RTL。

一个很好的例子来说明手势 RTL 支持是`SwipeableRow`

手势示例
// SwipeableRow.js
_isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
// ...
const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
return (
this._isSwipingRightFromClosed(gestureState) &&
gestureStateDx > RIGHT_SWIPE_THRESHOLD
);
},
动画示例
// SwipeableRow.js
_animateBounceBack(duration: number): void {
// ...
const swipeBounceBackDistance = IS_RTL ?
-RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
this._animateTo(
-swipeBounceBackDistance,
duration,
this._animateToClosedPositionDuringBounce,
);
},

维护支持 RTL 的应用程序

即使在最初的 RTL 兼容应用发布之后,您可能还需要迭代新功能。为了提高开发效率,`I18nManager`提供了`forceRTL()`函数,以便更快地进行 RTL 测试,而无需更改测试设备语言。您可能希望在您的应用中为此提供一个简单的开关。以下是 RNTester 中 RTL 示例的一个示例:

<RNTesterBlock title={'Quickly Test RTL Layout'}>
<View style={styles.flexDirectionRow}>
<Text style={styles.switchRowTextView}>forceRTL</Text>
<View style={styles.switchRowSwitchView}>
<Switch
onValueChange={this._onDirectionChange}
style={styles.rightAlignStyle}
value={this.state.isRTL}
/>
</View>
</View>
</RNTesterBlock>;

_onDirectionChange = () => {
I18nManager.forceRTL(!this.state.isRTL);
this.setState({isRTL: !this.state.isRTL});
Alert.alert(
'Reload this page',
'Please reload this page to change the UI direction! ' +
'All examples in this app will be affected. ' +
'Check them out to see what they look like in RTL layout.',
);
};

在开发新功能时,您可以轻松切换此按钮并重新加载应用程序以查看 RTL 布局。好处是您无需更改语言设置即可测试,但是某些文本对齐方式不会改变,如下一节所述。因此,在发布之前,始终建议您在 RTL 语言中测试您的应用程序。

限制和未来计划

RTL 支持应该涵盖您应用程序中的大部分用户体验;但是,目前存在一些限制:

  • 文本对齐行为在 Android 和 iOS 中有所不同
    • 在 iOS 中,默认文本对齐方式取决于活动的语言包,它们始终位于一侧。在 Android 中,默认文本对齐方式取决于文本内容的语言,即英语将左对齐,阿拉伯语将右对齐。
    • 理论上,这应该在跨平台保持一致,但有些人在使用应用程序时可能更喜欢一种行为而不是另一种。可能需要更多的用户体验研究来找出文本对齐的最佳实践。
  • 没有“真正的”左/右

    如前所述,我们将 JS 端的 `left`/`right` 样式映射到 `start`/`end`,RTL 布局中代码中的所有 `left` 在屏幕上变为“right”,代码中的 `right` 在屏幕上变为“left”。这很方便,因为您无需过多更改产品代码,但这意味着无法在代码中指定“真实左”或“真实右”。将来,允许组件独立于语言控制其方向可能是必要的。

  • 使手势和动画的 RTL 支持更具开发人员友好性

    目前,使手势和动画与 RTL 兼容仍需要一些编程工作。未来,理想的做法是找到一种方法,使手势和动画的 RTL 支持更具开发人员友好性。

试一试!

查看 `RNTester` 中的`RTLExample`,以了解有关 RTL 支持的更多信息,并告诉我们它的效果如何!

最后,感谢您的阅读!我们希望 React Native 对 RTL 的支持能帮助您为国际受众发展您的应用程序!

深入了解 React Native 性能

·阅读时长2分钟
Pieter De Baets
Facebook 软件工程师

React Native 允许您使用 React 和 Relay 的声明式编程模型,通过 JavaScript 构建 Android 和 iOS 应用程序。这使得代码更简洁、更易于理解;无需编译周期即可快速迭代;并且可以轻松地在多个平台之间共享代码。您可以更快地发布产品,并专注于真正重要的细节,使您的应用程序看起来和感觉都很棒。优化性能是其中的重要组成部分。以下是我们如何使 React Native 应用程序启动速度提高一倍的故事。

为何急于求成?

应用程序运行得更快,内容加载速度快,这意味着人们有更多时间与它互动,流畅的动画使应用程序使用起来令人愉悦。在新兴市场,2011 年级手机2G 网络上占多数,对性能的关注可能决定一个应用程序是否可用。

自发布 React Native for iOSAndroid 以来,我们一直在改进列表视图滚动性能、内存效率、UI 响应能力和应用程序启动时间。启动时间决定了应用程序的第一印象,并对框架的所有部分都造成压力,因此它是最具挑战性也最有回报的问题。

这是摘录。阅读其余帖子请访问 Facebook Code。