跳至主内容

1 篇文章 标记为 "engineering"

查看所有标签

React Native 月度动态 #4

· 1 分钟阅读
Mike Grabowski
Mike Grabowski
CTO and Co-Founder @ Callstack
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

React Native 月度会议持续进行中!以下是各团队的最新动态:

Callstack

  • React Native EU 已圆满落幕。来自 33 个国家的 300 余位参与者齐聚弗罗茨瓦夫。相关演讲可在 YouTube 频道观看。

  • 会后我们正逐步恢复开源项目进度。值得一提的是,即将发布的 react-native-opentok 新版本将修复现存的大部分问题。

GeekyAnts

我们正通过以下方式降低开发者采用 React Native 的门槛:

  • React Native EU 大会发布 BuilderX.io。BuilderX 是可直接处理 JavaScript 文件的设计工具(目前仅支持 React Native),能生成美观、可读且可编辑的代码。

  • 推出 ReactNativeSeed.com 提供多种 React Native 项目样板代码。包含 TypeScript & Flow 数据类型方案,MobX、Redux 和 mobx-state-tree 状态管理方案,以及 CRNA 和原生 React-Native 技术栈选项。

Expo

  • 即将发布 SDK 21,新增对 react-native 0.48.3 的支持,包含多项错误修复/稳定性提升/新功能:视频录制、全新启动屏 API、react-native-gesture-handler 支持及增强的错误处理机制。

  • 关于 react-native-gesture-handler:来自 Software MansionKrzysztof Magiera 持续推动该项目进展,我们协助进行了测试并资助了部分开发工作。SDK21 集成该库后,开发者可通过 Snack 轻松体验,期待看到大家的创意应用。

  • 关于错误日志/处理机制优化:日志改进细节请参考 Expo 内部 PR 摘要(重点关注"问题 2"),处理 npm 标准库模块导入失败的变更详见 此提交记录。React Native 上游仍有大量优化错误提示的空间,我们将跟进相关 PR,也欢迎社区参与贡献。

  • native.directory 持续扩展中,可通过 GitHub 仓库提交您的项目。

  • 正参与北美多地黑客马拉松活动,包括 PennAppsHack The NorthHackMIT,即将亮相 MHacks

Facebook

  • 正在优化 Android 平台的 <Text><TextInput> 组件(包括 <TextInput> 原生自适应高度、深层嵌套 <Text> 布局问题、代码结构优化及性能提升)。

  • 持续招募协助分类 issues 和处理 pull requests 的贡献者。

Microsoft

  • 发布了 CodePush 的代码签名功能。React Native 开发者现在可以为 CodePush 中的应用包进行签名。相关公告可在此查看

  • 正在完成 CodePush 与 Mobile Center 的集成工作,同时考虑整合测试/崩溃报告功能

下一次会议

下次会议定于 2017 年 10 月 10 日星期三举行。由于目前仅召开过四次会议,我们非常希望了解这些会议记录对 React Native 社区的价值。如果您对如何改进会议产出有任何建议,欢迎在 Twitter 上联系我

React Native 月报 #3

· 1 分钟阅读
Mike Grabowski
Mike Grabowski
CTO and Co-Founder @ Callstack
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

React Native 月度会议持续进行中!由于多数团队忙于产品交付,本月会议稍显简短。下个月我们将齐聚波兰弗罗茨瓦夫的 React Native EU 大会,记得抓紧购票届时现场相见!现在,让我们看看各团队的最新动态。

与会团队

本次第三次会议共有 5 支团队参与:

团队动态

以下是各团队带来的最新进展:

Callstack

  • 新近开源了 react-native-material-palette。该工具可从图像中提取主色调,助你打造视觉惊艳的应用。目前仅支持 Android 平台,我们正计划未来添加 iOS 支持。

  • 已在 haul 中实现 HMR 热更新支持,并带来诸多酷炫功能!欢迎查看最新版本。

  • React Native EU 2017 即将启幕!下个月将是 React Native 与波兰的盛宴!请速至此处抢购最后余票。

Expo

  • Snack 平台新增 npm 包安装支持(遵循常规 Expo 限制——依赖包不能调用 Expo 未集成的原生 API)。我们正着手实现多文件支持和资源上传功能。Satyajit 将在 React Native Europe 大会分享 Snack 相关技术。

  • 发布 SDK20 版本,新增相机、支付、安全存储、磁力计功能,支持文件下载暂停/恢复,并优化了启动/加载界面。

  • 正与 Krzysztof 持续合作开发 react-native-gesture-handler。诚邀各位尝试用其重构 PanResponder 或原生手势识别器实现的手势交互,并反馈遇到的问题。

  • 探索 JSC 调试协议实现,同时处理 Canny 平台上的多项功能需求。

Facebook

  • 上月我们讨论了 GitHub issue 跟踪管理机制,计划通过改进提升项目的可维护性。

  • 目前未解决问题数量稳定维持在 600 个左右,短期内可能保持该态势。过去一月我们关闭了 690 个长期无动态的 issue(定义为最近 60 天无任何评论)。其中 58 个被重新开启,原因包括:维护者承诺提供修复方案,或贡献者提出充分理由需保持 issue 开放。

  • 我们计划在可预见的未来继续推进自动化关闭陈旧 issue 的工作。理想状态下,我们希望追踪系统中每个有影响力的 issue 都能得到处理,但目前尚未达成这一目标。我们需要维护者们全力协助进行 issue 分类,确保不会遗漏那些导致回归问题或引入破坏性变更的 issue,特别是影响新建项目的 issue。有意协助的开发者可使用 Facebook GitHub Bot 参与 issue 和 PR 的分类工作。新版《维护者指南》包含了分类流程和 GitHub Bot 使用的详细信息。请将您添加至 issue 任务组,并鼓励其他活跃社区成员共同参与!

Microsoft

  • 新版 Skype 应用基于 React Native 构建,以实现跨平台最大程度的代码共享。这款采用 React Native 的 Skype 应用现已登陆 Android 和 iOS 应用商店。

  • 在基于 React Native 构建 Skype 应用的过程中,我们通过提交 PR 来修复遇到的缺陷和补充缺失功能。截至目前,我们已有 约 70 个 PR 被合并

  • React Native 让我们能够用同一套代码库驱动 Android 和 iOS 版 Skype 应用。我们还希望用该代码库支持 Skype 网页应用。为此我们构建并开源了位于 React/React Native 上层的轻量级框架 ReactXP。ReactXP 提供了一套跨平台组件:针对 iOS/Android 平台映射到 React Native,针对网页则映射到 react-dom。其目标与另一个开源库 React Native for Web 相似。关于两者实现差异的简要说明可参阅 ReactXP 常见问题

Shoutem

  • 我们持续致力于优化和简化使用 Shoutem 构建应用的开发者体验。

  • 已启动将所有应用迁移至 react-navigation 的工作,但因等待更稳定版本发布而暂缓推进,后续将评估原生导航方案的稳定性。

  • 正在将所有扩展组件及多数开源库(animation, theme, ui)升级至 React Native 0.47.1。

下一次会议

下次会议定于 2017 年 9 月 13 日星期三举行。由于目前仅举办了三期会议,我们想了解这些会议纪要对 React Native 社区的价值。如果您对会议产出形式有任何改进建议,欢迎通过 Twitter 与我联系。

Marketplace 中的 React Native 性能优化

· 1 分钟阅读
Facebook 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

React Native 在 Facebook 生态的多个应用中均有使用,包括主应用中的顶层标签页。本文重点讨论高曝光产品 Marketplace。该服务已在十余个国家上线,让用户能发现其他用户提供的商品和服务。

2017 年上半年,通过 Relay 团队、Marketplace 团队、移动 JS 平台团队和 React Native 团队的协作,我们将 Android 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 模块能降低初始化成本,因此将多个模块集中打包。然而实际证明,预加载和集中这些模块的成本远超收益。通过重新配置内联请求黑名单并从启动路径移除 JavaScript 模块,我们成功避免了加载非必要模块(例如在只需使用 Relay Modern 时加载了 Relay Classic)。如今我们的 RUN_JS_BUNDLE 分解指标速度提升了 75% 以上。

我们还通过优化特定产品的原生模块获得了性能提升。例如通过延迟注入某个原生模块的依赖项,将其成本降低了 98%。通过消除 Marketplace 启动与其他产品之间的资源争用,启动时间获得了同等幅度的缩减。

最值得称道的是,这些改进中有许多可广泛应用于所有基于 React Native 构建的界面。

结语

人们通常认为 React Native 的启动性能问题源于 JavaScript 运行缓慢或网络延迟过高。虽然提升 JavaScript 执行速度确实能显著降低 TTI(交互时间),但这些因素的实际影响比例远低于先前的预想。

当前获得的经验是:测量、测量、再测量!部分优化成果来自将运行时成本转移到构建阶段,例如 Relay Modern 和 Lazy NativeModules;部分成果来自通过更智能的代码并行化或删除无用代码来避免工作负载;还有部分成果则来自 React Native 的重大架构调整(如清理线程阻塞)。性能优化没有万能方案,长期成效将源自持续的检测与改进。切勿让认知偏差左右决策,而应通过严谨收集和解读生产环境数据来指导后续工作。

未来计划

长期目标是让 Marketplace 的 TTI 达到与原生构建的同类产品相当的水平,并实现 React Native 整体性能与原生方案持平。尽管过去半年我们将 React Native 桥接的启动成本降低了约 80%,但我们将通过 Prepack 等项目及更多构建时处理技术,最终将桥接成本降至趋近于零。

React Native 月度动态 #2

· 1 分钟阅读
Tomislav Tenodi
Shoutem 产品经理
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

React Native 月度会议仍在继续!本次会议我们有幸邀请到 Infinite Red 团队参与,他们是 Chain React 大会(专注 React Native 的技术会议)的幕后智囊。由于多数与会者当时正在 Chain React 发表演讲,我们将会议顺延了一周。大会演讲视频已上线发布,强烈推荐大家观看。现在,让我们看看各团队的最新动态。

与会团队

第二次会议共有 9 支团队参与:

团队动态

以下是各团队带来的最新进展:

Airbnb

Callstack

  • Mike Grabowski 持续管理 React Native 的月度版本发布,包括多个测试版。尤其值得一提的是,他正在推进 v0.43.5 版本在 npm 的发布,该版本将解决 Windows 用户的关键阻塞问题!

  • Haul 项目正稳步推进:已合并添加 HMR 的 pull request,并发布了多项改进。近期成功获得多位行业领袖的采用,未来可能启动该领域的全职商业化开发

  • 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 团队。若您认为该项目有价值,请参与共建!

  • 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 Miskiewiczreact-navigation 中成功模拟了 UINavigationController 转场效果,详见其推文演示,新版即将发布。另可关注他提交的 MaskedViewIOS 上游代码。若您具备能力,欢迎贡献 Android 版 MaskedView 实现!

Facebook

  • Facebook 内部正探索在 React Native 中嵌入原生 ComponentKitLitho 组件的方案

  • 非常欢迎大家为 React Native 做贡献!如果您想知道如何参与,我们的《贡献指南》详细说明了开发流程和提交首次 PR 的步骤。除了编写代码外,您还可以通过分类 issue 或更新文档等方式做出贡献。

    • 截至本文撰写时,React Native 有 635待处理 issue249待合并 PR。这对维护者来说是巨大的负担,即使问题在内部修复后,也很难确保及时更新相关任务状态。
    • 我们正在探索既能保持社区满意度又能有效管理的方式。可能的方案包括:关闭陈旧 issue、授予更多协作者管理权限、自动关闭不符合模板的 issue 等(但不仅限于此)。我们编写了《维护者职责说明》来明确预期,避免沟通落差。如果您有改善维护者体验的建议,或能让 issue/PR 提交者感受到重视的方案,请随时告知!

GeekyAnts

  • 我们在 Chain React 大会上演示了适用于 React Native 文件的设计工具,许多参会者加入了等候名单。

  • 我们正在研究其他跨平台方案如 Google Flutter(即将发布深度对比)、Kotlin NativeApache Weex,通过理解架构差异来优化 React Native 的整体性能。

  • 已在多数应用中切换至 react-navigation,显著提升了整体性能。

  • 同时发布了 NativeBase 应用市场——面向开发者的 React Native 组件与应用交易平台。

Infinite Red

Microsoft

  • CodePush 已整合至 Mobile Center,现有用户工作流不受影响。

    • 部分用户反馈存在应用重复问题(已在 Mobile Center 创建过应用)。我们正在修复,若您遇到此情况请告知,我们将为您合并应用。
  • Mobile Center 现支持 CodePush 的推送通知功能。我们还演示了如何结合通知与 CodePush 进行 A/B 测试——这是 React Native 架构独有的能力。

  • VS Code 的 React Native 调试功能存在已知问题,扩展程序将在数日内发布修复版本。

  • 鉴于微软内部多个团队都在使用 React Native,我们将协调更多小组代表参与下次会议。

Shoutem

  • 已完成 Shoutem 平台的 React Native 开发优化,现在您可以使用所有标准 react-native 命令进行应用开发。

  • 我们在性能分析方面做了大量探索工作,试图找到最佳实践方案。目前很大一部分性能文档已过时,我们将尽力向官方文档提交PR更新,或者至少通过博文分享我们的研究成果。

  • 正在将导航方案切换至react-navigation,预计很快会提供使用反馈。

  • 在工具包中发布了新版HTML组件,可将原始HTML转换为React Native组件树。

Wix

  • 已着手准备向Metro打包工具提交PR,包含react-native-repackager的功能特性。我们更新了该工具以支持RN 44版本(生产环境正在使用),主要服务于detox的模拟测试基础设施。

  • 过去三周持续为Wix应用添加detox测试覆盖。在这个超40名工程师参与的大型项目中,我们积累了宝贵的自动化测试经验来替代人工QA。期间解决了detox的若干问题,新版已发布。很高兴向各位报告,我们严格遵循"零波动策略",目前测试通过率保持稳定。

  • Android版detox进展顺利,社区贡献显著。预计两周内发布初始版本。

  • 性能测试工具DetoxInstruments的规模超出预期。现计划将其拆分为独立工具,不再与detox强耦合,使其能通用分析iOS应用性能。后续将与detox集成,实现对性能指标的自动化测试。

下一次会议

下次会议定于2017年8月16日举行。由于目前仅举办过两次月度会议,我们非常期待了解这些会议纪要对React Native社区的实际价值。如有改进建议,欢迎通过Twitter随时与我交流。

React Native 月刊 #1

· 1 分钟阅读
Tomislav Tenodi
Shoutem 产品经理
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

Shoutem,我们有幸从最初就开始使用 React Native。我们决心从第一天就成为这个卓越社区的一员。很快我们发现,要跟上社区飞速发展和改进的步伐几乎是不可能的。因此我们决定组织月度会议,让所有主要的 React Native 贡献者能够简要介绍他们的工作成果和计划。

月度会议

我们在 2017 年 6 月 14 日举行了首次月度会议。React Native 月刊的使命简单明确:推动 React Native 社区进步。通过展示各团队的工作成果,促进线下团队协作。

与会团队

首次会议共有 8 个团队参与:

期待更多核心贡献者加入后续会议!

团队动态

考虑到各团队的计划可能引起广泛关注,我们将在 React Native 博客分享这些内容。以下是具体动态:

Airbnb

  • 计划为 View 组件和 AccessibilityInfo 原生模块增加无障碍(A11y)API

  • 研究为 Android 原生模块添加 API 以支持指定运行线程

  • 探索初始化性能优化方案

  • 研究在 "unbundle" 基础上更先进的打包策略

Callstack

  • 计划使用 Detox 进行端到端测试以改进发布流程,相关 PR 即将提交

  • Blob 相关 PR 已合并,后续 PR 正在准备中

  • 在内部项目中增加 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

  • 团队正在开发代号 Builder 的 UI/UX 设计工具,可直接操作 .js 文件。目前仅支持 React Native,功能类似 Adobe XD 和 Sketch。

  • 团队正努力实现:在编辑器中加载现有 React Native 应用后,设计师可直接进行可视化修改并实时保存至 JS 文件。

  • 致力于弥合设计师与开发者之间的鸿沟,推动双方在同一个代码库协作。

  • 此外,NativeBase 项目近期在 GitHub 上突破 5,000 星标。

Microsoft

  • CodePush 已集成至 Mobile Center。这是实现分发、分析等服务深度整合的第一步,详见公告

  • VS Code 的调试功能存在 bug,团队正在修复并将发布新版本。

  • 正在评估将 Detox 用于集成测试,并探索通过 JSC Context 在崩溃报告中捕获变量。

Shoutem

  • 简化使用 React Native 社区工具开发 Shoutem 应用的流程。开发者将能直接使用所有 React Native 命令运行 Shoutem 创建的应用。

  • 研究 React Native 性能分析工具。团队在配置过程中积累了大量经验,后续将分享实践心得。

  • 致力于简化 React Native 与现有原生应用的集成方案,计划公开内部研发的集成架构以获取社区反馈。

Wix

  • 内部正全面采用 Detox,推动 Wix 应用核心模块实现"零人工 QA"。Detox 已被数十名开发者在生产环境高频使用,成熟度快速提升。

  • 正在为 Metro Bundler 添加构建时覆盖任意文件扩展名的支持。除了"ios"和"android"外,还将支持"e2e"或"detox"等自定义扩展名。计划将此用于端到端(E2E)模拟测试。已有名为 react-native-repackager 的库,现正开发PR提交。

  • 研究性能测试自动化方案。新仓库 DetoxInstruments 已开放源码,欢迎查阅。

  • 正与KPN的贡献者合作开发Detox的Android版本及真机支持方案。

  • 考虑构建"Detox即平台"架构,支持开发需要自动化模拟器/设备的工具。典型案例包括React Native版Storybook及Ram提出的集成测试方案。

下一次会议

会议每四周举行一次,下次会议定于2017年7月12日。鉴于该会议机制刚刚启动,我们非常希望了解这些记录对React Native社区的价值。欢迎通过Twitter随时联系我,提出后续会议应涵盖的主题或改进会议成果的建议。

React Native 中更优的列表视图

· 1 分钟阅读
Spencer Ahrens
Facebook 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

自从我们在社区群组的预告发布后,许多开发者已经开始尝试使用新的列表组件。今天我们正式宣布:告别繁琐的 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)

  • 可配置的可视区域回调 (onViewableItemsChanged / viewabilityConfig)

  • 横向模式 (horizontal)

  • 智能行项与分节分隔符

  • 多列布局 (numColumns)

  • 精准滚动控制 (scrollToEnd, scrollToIndex, scrollToItem)

  • 增强的 Flow 类型支持

注意事项

  • 当行项子树滚动出渲染窗口时,其内部状态不会被保留。请确保所有数据都存储在行项数据或外部状态库(如 Flux、Redux 或 Relay)中

  • 这些组件基于 PureComponent 实现,意味着当 props 保持浅层相等时不会重新渲染。请确保 renderItem 函数直接依赖的所有数据都通过 props 传递,且更新后引用发生变化(非 === 相等),否则界面可能不会响应变更。这包括 data 属性和父组件状态。例如:

    <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 属性作为 React 键值。您也可通过自定义 keyExtractor 属性指定键值生成逻辑。

性能表现

新版列表组件在简化 API 的同时实现了显著的性能提升,核心突破在于无论行数多少都能保持近乎恒定的内存占用。这是通过「虚拟化」技术实现的:当元素滚出渲染窗口时,会将其从组件树中完全卸载,释放 React 组件的 JS 内存、影子树及原生视图的内存。请注意:组件内部状态不会被保留,务必通过 Relay/Redux/Flux 等外部状态管理工具保存关键数据。

限制渲染窗口还减少了 React 和原生平台的工作量(例如视图遍历)。即使渲染百万级列表的末尾项,也无需遍历全部元素。您甚至可通过 scrollToIndex 直接跳转至列表中部,而无需额外渲染。

我们改进了渲染调度机制以提升应用响应性:位于渲染窗口边缘的项会在手势/动画等交互完成后,以较低优先级进行非频繁渲染。

高级用法

ListView 不同,新版列表在任意属性变更时会重新渲染窗口内的所有项。由于窗口渲染保持了恒定数量,通常不会影响性能。但若列表项结构复杂,建议在子组件中使用 React.PureComponentshouldComponentUpdate 遵循 React 性能优化实践。

若能在不实际渲染的情况下预计算行高,可通过 getItemLayout 属性提升用户体验。这将使 scrollToIndex 等跳转操作更流畅,并优化滚动指示器显示(无需渲染即可确定内容高度)。

若使用不可变列表等特殊数据结构,请选用 <VirtualizedList> 组件。它通过 getItem 属性支持按索引获取数据,并具有更宽松的 Flow 类型约束。

我们还提供多项参数应对特殊场景:通过 windowSize 平衡内存占用与用户体验;用 maxToRenderPerBatch 调节填充速度与响应性;通过 onEndReachedThreshold 控制滚动加载触发时机等。

未来计划

  • 迁移现有场景(最终弃用 ListView

  • 按需求增补功能(欢迎反馈!)

  • 粘性分组头部支持

  • 深度性能优化

  • 支持带状态的函数式列表项组件

idx:存在函数

· 1 分钟阅读
Timothy Yung
Facebook 工程经理
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

在 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 函数会捕获因访问 null 或 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。使用方式为:安装 idxbabel-plugin-idx npm 包,并在 .babelrc 文件的插件列表中添加 "idx"。

介绍 Create React Native App

· 1 分钟阅读
Adam Perry
Expo 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

今天我们很高兴地宣布推出 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 请替换相应命令):

$ 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 也支持 "ejecting"(弹出)操作。

运行 npm run eject 即可获得与 react-native init 生成项目相似的结构。此时您将需要 Xcode 和/或 Android Studio,就像从 react-native init 开始项目一样。通过 react-native link 添加库的功能将恢复可用,您也将完全掌控原生代码的编译过程。

问题或反馈?

Create React Native App 现已足够稳定以供日常使用,这意味着我们非常期待听到您的使用体验!您可以通过 Twitter 联系我,或在 GitHub 仓库 提交 issue。我们非常欢迎 Pull Request!

使用原生驱动优化 Animated 动画性能

· 1 分钟阅读
Janic Duplessis
App & Flow 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

过去一年,我们持续优化基于 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 的属性节点

  • JS:通过 setNativeProps 更新 View

  • 跨越 JS 到原生桥接层

  • Native:直接更新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',
});

创建原生属性节点,用于告知原生驱动其关联的视图属性:

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

将节点连接起来:

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

将属性节点关联到视图:

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

至此,原生动画模块已具备直接更新原生视图所需的全部信息,无需通过JS线程计算任何值。

最后只需通过指定动画曲线类型和待更新的动画值来启动动画。时序动画可通过在JS中预计算所有动画帧来简化实现,从而减小原生端代码量。

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

动画运行时实际流程如下:

  • Native:原生动画驱动使用CADisplayLink(iOS)或android.view.Choreographer(Android)逐帧执行,根据动画曲线计算新值并更新驱动的动画值

  • Native:计算中间值并传递给关联原生视图的属性节点

  • Native:直接更新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的所有功能。主要限制包括:

  • 仅支持非布局属性动画(如transformopacity生效,Flexbox和位置属性无效)
  • Animated.event仅支持直接事件(如ScrollView#onScroll),不支持冒泡事件(故无法用于PanResponder

原生动画驱动虽已集成在React Native中较长时间,但因处于实验阶段从未正式文档化。建议使用React Native 0.40+版本以获取稳定支持

扩展资源

推荐观看Christopher Chedeau专题讲座深入了解Animated

若需深度探讨动画技术及原生化如何提升用户体验,可观看Krzysztof Magiera技术分享

为 React Native 应用提供从右到左布局支持

· 1 分钟阅读
Mengjue (Mandy) Wang
Facebook 软件工程实习生
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

将应用发布到应用商店后,国际化是扩大受众范围的下一步。全球有超过 20 个国家和众多人口使用从右到左(RTL)的语言。因此,为这些用户提供 RTL 支持是必要的。

我们很高兴地宣布,React Native 已经改进以支持 RTL 布局。该功能现在已在 react-native 的主分支中提供,并将在下一个候选版本(RC)中发布:v0.33.0-rc

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

为了在生产环境中测试 RTL 支持,最新版本的 Facebook Ads Manager 应用(首个跨平台 100% RN 应用)现已提供阿拉伯语和希伯来语的 RTL 布局,支持 iOSAndroid。以下是这些 RTL 语言下的效果:

RN 中支持 RTL 的更改概述

css-layout 的布局中已有 startend 的概念。在从左到右(LTR)布局中,start 表示 leftend 表示 right。但在 RTL 中,start 表示 rightend 表示 left。这意味着我们可以让 RN 依赖 startend 的计算来确定正确的布局,包括 positionpaddingmargin

此外,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 应用,你需要在 AndroidManifest.xml 文件的 <application> 元素中添加 android:supportsRtl="true" 属性。

现在,当你重新编译应用并将设备语言切换为 RTL 语言(如阿拉伯语或希伯来语)时,应用布局应会自动切换为 RTL 模式。

编写支持 RTL 的组件

通常情况下,大多数组件已原生支持 RTL 布局,例如:

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

但需要注意几种特殊情况,这时你需要使用 I18nManager。在 I18nManager 中,常量 isRTL 可判断当前布局是否为 RTL,从而进行相应调整。

具有方向含义的图标

当组件包含图标或图像时,它们在 LTR 和 RTL 布局中的显示方向不会自动改变。因此你需要根据布局手动调整方向:

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

以下两种方法可实现图标方向切换:

  • 为图像组件添加 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 相反。目前在 React Native 中,手势和动画的支持不在核心代码层,而是在组件层。好消息是部分组件(如 SwipeableRowNavigationExperimental)已支持 RTL 布局。但其他涉及手势的组件仍需手动适配。

SwipeableRow 是演示 RTL 手势支持的典型范例。

手势示例
// 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 显示为"左"。这减少了代码修改量,但也意味着无法在代码中指定"绝对左侧"或"绝对右侧"。未来可能需要支持组件独立控制布局方向。

  • 提升手势和动画的 RTL 开发体验

    当前实现手势和动画的 RTL 兼容性仍需较多编程工作。未来将探索更友好的开发方案。

立即尝试!

查看 RNTester 中的 RTLExample 深入了解 RTL 支持,欢迎反馈使用体验!

最后,感谢您的阅读!希望 React Native 的 RTL 支持能帮助您拓展国际用户群体!