跳到主要内容

React Native 每月精选 #3

·5 分钟阅读
Mike Grabowski
Mike Grabowski
Callstack 的 CTO 和联合创始人

React Native 月度会议继续进行!本月的会议时间稍短,因为我们的大部分团队都在忙于交付。下个月,我们将参加在波兰弗罗茨瓦夫举行的 React Native EU 会议。请务必抢购门票,我们在那里与您亲自见面!与此同时,让我们看看我们的团队在做什么。

团队

在第三次会议上,我们有 5 个团队加入我们

笔记

以下是每个团队的笔记

Callstack

  • 最近开源了 react-native-material-palette。它可以从图像中提取突出颜色,以帮助您创建视觉上引人入胜的应用。目前仅支持 Android,但我们正在研究在未来增加对 iOS 的支持。
  • 我们已将 HMR 支持添加到 haul 以及许多其他很酷的功能!查看最新版本。
  • React Native EU 2017 即将到来!下个月将全是关于 React Native 和波兰!请务必在此处抢购最后可用的门票 here

Expo

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

Facebook

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

Microsoft

  • 新的 Skype 应用构建在 React Native 之上,以便尽可能多地在平台之间共享代码。基于 React Native 的 Skype 应用目前已在 Android 和 iOS 应用商店中提供。
  • 在基于 React Native 构建 Skype 应用时,我们会向 React Native 发送拉取请求,以解决我们遇到的错误和缺失的功能。到目前为止,我们已经合并了大约 70 个拉取请求
  • React Native 使我们能够从同一代码库为 Android 和 iOS Skype 应用提供支持。我们还希望使用该代码库为 Skype Web 应用提供支持。为了帮助我们实现这一目标,我们在 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 上 ping 我。

React Native 在 Marketplace 中的性能

·5 分钟阅读
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,该 JavaScript 确定 React Native 将在原生处理时间中渲染哪些原生视图。

不同的方法

我们早期犯下的错误之一是让 Systrace 和 CTScan 驱动我们的性能工作。这些工具帮助我们在 2016 年找到了许多唾手可得的成果,但我们发现 Systrace 和 CTScan 都不能代表生产场景,也无法模拟野外发生的情况。时间细分的比率通常是不正确的,有时甚至严重偏离。在极端情况下,我们期望花费几毫秒的一些事情实际上花费了数百或数千毫秒。尽管如此,CTScan 还是有用的,我们发现它可以捕获生产环境中三分之一的回归。

在 Android 上,我们将这些工具的缺点归因于以下事实:1) React Native 是一个多线程框架,2) Marketplace 与大量复杂视图(例如 Newsfeed 和其他顶级标签页)位于同一位置,以及 3) 计算时间变化很大。因此,这半年,我们让生产测量和细分驱动了我们几乎所有的决策和优先级排序。

走向生产工具化之路

在表面上,生产工具化听起来很简单,但事实证明这是一个非常复杂的过程。它花费了多个 2-3 周的迭代周期;由于将提交落地到 master、将应用推送到 Play 商店以及收集足够的生产样本以对我们的工作充满信心都存在延迟。每个迭代周期都涉及发现我们的细分是否准确、是否具有正确的粒度级别以及它们是否正确地加总到整个时间跨度。我们不能依赖 alpha 和 beta 版本,因为它们不能代表一般人群。本质上,我们非常繁琐地构建了一个非常准确的生产跟踪,该跟踪基于数百万个样本的聚合。

我们一丝不苟地验证细分中的每毫秒是否正确地加总到其父指标的原因之一是,我们很早就意识到我们的工具化存在差距。事实证明,我们最初的细分没有考虑到线程跳转造成的停顿。线程跳转本身并不昂贵,但跳转到已经在忙碌工作的繁忙线程非常昂贵。我们最终通过在正确的时刻散布 Thread.sleep() 调用在本地重现了这些阻塞,并通过以下方法设法修复了它们

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

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

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

我们还通过调查特定于产品的原生模块发现了优势。例如,通过延迟注入原生模块的依赖项,我们将该原生模块的成本降低了 98%。通过消除 Marketplace 启动与其他产品的竞争,我们将启动时间缩短了相当的时间间隔。

最好的部分是,许多这些改进广泛适用于使用 React Native 构建的所有屏幕。

结论

人们认为 React Native 启动性能问题是由 JavaScript 速度慢或网络时间过高引起的。虽然加速 JavaScript 等操作会使 TTI 降低相当可观的总和,但与之前认为的相比,这些操作对 TTI 的贡献百分比要小得多。

到目前为止的教训是测量,测量,再测量! 一些成功来自于将运行时成本转移到构建时,例如 Relay Modern 和 Lazy NativeModules。其他成功来自于通过更智能地并行化代码或删除死代码来避免工作。还有一些成功来自于对 React Native 的大型架构更改,例如清理线程阻塞。性能没有宏大的解决方案,而更长期的性能提升将来自于增量工具化和改进。不要让认知偏差影响您的决策。相反,请仔细收集和解释生产数据,以指导未来的工作。

未来计划

从长远来看,我们希望 Marketplace TTI 可以与使用 Native 构建的类似产品相媲美,并且总的来说,React Native 性能可以与原生性能相媲美。此外,尽管这半年我们将 bridge 启动成本大幅降低了约 80%,但我们计划通过 Prepack 和更多构建时处理等项目,将 React Native bridge 的成本降至接近于零。

React Native 每月精选 #2

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

React Native 月度会议继续进行!在本次会议上,Infinite Red 加入了我们,他们是 Chain React,React Native 会议 背后的伟大人物。由于这里的大多数人都在 Chain React 上发表演讲,我们将会议推迟了一周。会议的演讲已发布到网上,我鼓励您查看它们。那么,让我们看看我们的团队在做什么。

团队

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

笔记

以下是每个团队的笔记

Airbnb

  • 查看 Airbnb 仓库 以获取与 React Native 相关的项目。

Callstack

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

Expo

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

Facebook

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

GeekyAnts

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

Infinite Red

Microsoft

  • CodePush 现在已集成到 Mobile Center 中。现有用户的工作流程不会发生任何变化。
    • 有些人报告了应用重复的问题 - 他们在 Mobile Center 上已经有一个应用。我们正在努力解决这些问题,但如果您有两个应用,请告知我们,我们可以为您合并它们。
  • Mobile Center 现在支持 CodePush 的推送通知。我们还展示了如何将通知和 CodePush 结合使用来进行 A/B 测试应用 - 这是 ReactNative 架构独有的功能。
  • VS Code 在 ReactNative 调试方面存在已知问题 - 几天后发布的扩展程序的下一个版本将修复此问题。
  • 由于 Microsoft 内部还有许多其他团队也在开发 React Native,我们将努力在下次会议上获得所有小组更好的代表性。

Shoutem

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

Wix

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

下一次会议

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

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 测试来改进发布流程。Pull request 应该很快就会提交。
  • 他们一直在处理的 Blob pull request 已合并,后续的 pull request 即将到来。
  • 正在内部项目中增加 Haul 的采用率,以了解其与 Metro Bundler 相比的性能。正在与 webpack 团队合作,以实现更好的多线程性能。
  • 在内部,他们已经实施了更好的基础设施来管理开源项目。计划在未来几周内发布更多内容。
  • React Native Europe 会议即将到来,目前还没有什么有趣的事情,但邀请大家参加!
  • 暂时退出了 react-navigation,以研究替代方案(尤其是原生导航)。

Expo

Facebook

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

GeekyAnts

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

Microsoft

  • CodePush 现在已集成到 Mobile Center 中。这是提供与分发、分析和其他服务更集成体验的第一步。请参阅他们的公告此处
  • VS Code 在调试方面存在一个 bug,他们正在努力修复该 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 上 ping 我。

React Native 中更好的列表视图

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

在我们在社区群组中发布预告公告后,你们中的许多人已经开始试用我们的一些新的 List 组件,但我们今天正式发布它们!不再有 ListViewDataSource,过时的行,被忽略的 bug 或过多的内存消耗 - 在最新的 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)
  • scrollToEnd, scrollToIndex, 和 scrollToItem
  • 更好的 Flow 类型。

一些注意事项

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

  • 这些组件基于 PureComponent,这意味着如果 props 保持浅相等,它们将不会重新渲染。确保您的 renderItem 函数直接依赖的所有内容都作为 props 传递,并且在更新后不是 ===,否则您的 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 key。或者,您可以提供自定义 keyExtractor prop。

性能

除了简化 API 之外,新的列表组件还具有显着的性能增强,主要的增强是对于任何数量的行,内存使用量几乎恒定。这是通过“虚拟化”渲染窗口之外的元素来实现的,方法是将它们从组件层次结构中完全卸载,并回收来自 react 组件的 JS 内存,以及来自阴影树和 UI 视图的本机内存。这有一个缺点,即内部组件状态将不会被保留,因此请确保您在组件本身之外跟踪任何重要状态,例如在 Relay 或 Redux 或 Flux 存储中。

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

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

高级用法

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

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

如果您有另一种数据类型,例如不可变列表,则 <VirtualizedList> 是最佳选择。它采用 getItem prop,使您可以返回任何给定索引的项目数据,并且具有更宽松的 flow 类型。

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

未来工作

  • 现有表面的迁移(最终弃用 ListView)。
  • 更多功能,因为我们看到/听到需求(请告诉我们!)。
  • 粘性节标题支持。
  • 更多性能优化。
  • 支持带有状态的功能项目组件。

idx:存在性函数

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

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

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

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 函数将 try-catch 由于访问 null 或 undefined 上的属性而导致的错误。如果捕获到此类错误,它将返回 null 或 undefined。(您可以查看如何在 idx.js 中实现这一点。)

实际上,try-catch 每个嵌套属性访问都很慢,并且区分特定类型的 TypeErrors 是脆弱的。为了解决这些缺点,我们创建了一个 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 上可用。它们通过安装 idxbabel-plugin-idx npm 包,并将 “idx” 添加到您的 .babelrc 文件中的插件列表中来使用。

介绍 Create React Native App

·2 分钟阅读
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 打包器并打印一个 QR 码。在 Expo app 中打开它以加载您的 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 仓库 上打开一个 issue。非常欢迎 Pull requests!

为 Animated 使用 Native Driver

·7 分钟阅读
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 的支持,以及解决我们在生产应用中使用它时发现的 bug。这真的是一项社区努力,我要感谢所有参与其中的人以及 Expo 对大部分开发的赞助。React Native 中的 Touchable 组件以及新发布的 React Navigation 库中的导航动画现在都使用了它。

它是如何工作的?

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

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

  • 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 节点,这告诉原生驱动程序它附加到的视图上的哪个 prop

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();

动画值仅与一个驱动程序兼容,因此如果您在启动动画时使用了 native driver,请确保该值上的每个动画也使用 native driver。

它也适用于 Animated.event,如果您有一个动画必须跟随滚动位置,这将非常有用,因为如果没有 native driver,由于 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 支持。 主要限制是您只能为非布局属性设置动画,例如 transformopacity 可以工作,但 Flexbox 和 position 属性则不行。 另一个限制是 Animated.event,它仅适用于直接事件,而不适用于冒泡事件。 这意味着它不适用于 PanResponder,但适用于 ScrollView#onScroll 等。

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

资源

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

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

每月发布节奏:发布 12 月和 1 月 RC

·2 分钟阅读
Eric Vicenti
Facebook 工程师

在 React Native 推出后不久,我们开始每两周发布一次版本,以帮助社区采用新功能,同时保持版本 для 生产使用的稳定性。 在 Facebook,我们必须每两周稳定一次代码库,以便发布我们的生产 iOS 应用,因此我们决定以相同的节奏发布开源版本。 现在,许多 Facebook 应用每周发布一次,尤其是在 Android 上。 因为我们每周从 master 分支发布,所以我们需要保持其相当稳定。 因此,双周发布节奏甚至不再使内部贡献者受益。

我们经常听到社区的反馈,认为发布频率难以跟上。 像 Expo 这样的工具不得不跳过每隔一个版本,以便管理版本的快速变化。 因此,很明显,双周发布对社区没有好处。

现在每月发布

我们很高兴宣布新的每月发布节奏,以及 2016 年 12 月的发布版本 v0.40,该版本在上个月一直在稳定,可以采用。(只需确保 更新 iOS 上的原生模块中的标头)。

虽然它可能会因避开周末或处理意外问题而有所变化几天,但您现在可以预期给定的版本将在每月的第一天可用,并在月末发布。

使用当月版本以获得最佳支持

一月份的候选发布版本已准备好试用,您可以 在此处查看新功能

为了了解即将发生的变化并向 React Native 贡献者提供更好的反馈,请尽可能始终使用当月的候选发布版本。 到每个版本在月末发布时,它包含的更改将在 Facebook 生产应用中发布超过两周。

您可以使用新的 react-native-git-upgrade 命令轻松升级您的应用

npm install -g react-native-git-upgrade
react-native-git-upgrade 0.41.0-rc.0

我们希望这种更简单的方法将使社区更容易跟踪 React Native 中的更改,并尽快采用新版本!

(感谢 Martin Konicek 提出此计划,并感谢 Mike Grabowski 使之成为现实)

得益于 Git,升级更轻松

·4 分钟阅读
Nicolas Cuillery
Zenika 的 JavaScript 顾问和培训师

升级到新版本的 React Native 一直很困难。 您之前可能见过类似这样的情况

这些选项都不是理想的。 通过覆盖文件,我们会丢失本地更改。 通过不覆盖,我们无法获得最新的更新。

今天,我很自豪地推出一个新工具,它可以帮助解决这个问题。 该工具名为 react-native-git-upgrade,并在幕后使用 Git 来尽可能自动解决冲突。

用法

要求:Git 必须在 PATH 中可用。 您的项目不必由 Git 管理。

全局安装 react-native-git-upgrade

$ npm install -g react-native-git-upgrade

或者,使用 Yarn

$ yarn global add react-native-git-upgrade

然后,在您的项目目录中运行它

$ cd MyProject
$ react-native-git-upgrade 0.38.0

注意:不要运行 'npm install' 来安装新版本的 react-native。 该工具需要能够比较旧的和新的项目模板才能正确工作。 只需在您的应用文件夹中运行它,如上所示,同时仍使用旧版本。

示例输出

您也可以运行不带参数的 react-native-git-upgrade 以升级到最新版本的 React Native。

我们尝试保留您在 Android 和 iOS 构建文件中的更改,因此您在升级后无需运行 react-native link

我们已将实现设计为尽可能少地侵入。 它完全基于在临时目录中即时创建的本地 Git 存储库。 它不会干扰您的项目存储库(无论您使用哪种 VCS:Git、SVN、Mercurial ... 或不使用)。 如果发生意外错误,您的源文件将被恢复。

它是如何工作的?

关键步骤是生成 Git 补丁。 该补丁包含您的应用正在使用的版本和新版本之间 React Native 模板中所做的所有更改。

为了获得此补丁,我们需要从 react-native 包内的 node_modules 目录中嵌入的模板生成一个应用(这些模板与 react-native init 命令使用的模板相同)。 然后,在从当前版本和新版本中的模板生成原生应用后,Git 能够生成一个适用于您的项目的补丁(即包含您的应用名称)。

[...]

diff --git a/ios/MyAwesomeApp/Info.plist b/ios/MyAwesomeApp/Info.plist
index e98ebb0..2fb6a11 100644
--- a/ios/MyAwesomeApp/Info.plist
+++ b/ios/MyAwesomeApp/Info.plist
@@ -45,7 +45,7 @@
<dict>
<key>localhost</key>
<dict>
- <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
[...]

我们现在需要做的就是将此补丁应用于您的源文件。 虽然旧的 react-native upgrade 过程会提示您注意任何小的差异,但 Git 能够使用其 3 向合并算法自动合并大多数更改,并最终为我们留下熟悉的冲突分隔符。

    13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
<<<<<<< ours
CODE_SIGN_IDENTITY = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/HockeySDK.embeddedframework",
"$(PROJECT_DIR)/HockeySDK-iOS/HockeySDK.embeddedframework",
);
=======
CURRENT_PROJECT_VERSION = 1;
>>>>>>> theirs
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-code-push/ios/CodePush/**",
);

这些冲突通常很容易理解。 分隔符 ours 代表“您的团队”,而 theirs 可以被视为“React Native 团队”。

为什么引入新的全局包?

React Native 附带一个全局 CLI(react-native-cli 包),它将命令委托给嵌入在 node_modules/react-native/local-cli 目录中的本地 CLI。

正如我们上面提到的,该过程必须从您当前的 React Native 版本开始。 如果我们将实现嵌入到 local-cli 中,那么在使用旧版本的 React Native 时,您将无法享受此功能。 例如,如果此新的升级代码仅在 0.38.0 中发布,您将无法从 0.29.2 升级到 0.38.0。

基于 Git 的升级是开发人员体验的重大改进,并且让每个人都可以使用它非常重要。 通过使用单独的包 react-native-git-upgrade 全局安装,无论您的项目使用哪个版本的 React Native,您今天都可以使用此新代码。

另一个原因是 Martin Konicek 最近的 Yeoman 淘汰。 我们不想将这些 Yeoman 依赖项放回 react-native 包中,以便能够评估旧模板以创建补丁。

试用并提供反馈

作为结论,我想说,享受此功能,并随时 提出改进建议、报告问题,尤其是 发送拉取请求。 每个环境都略有不同,每个 React Native 项目也各不相同,我们需要您的反馈才能使此功能对每个人都有效。

谢谢!

我要感谢伟大的公司 ZenikaM6 Web (已存档),没有他们,这一切都不可能实现!