跳至主内容

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技术分享

月度发布周期:发布十二月和一月候选版

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

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

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 实现更轻松的升级

· 1 分钟阅读
Nicolas Cuillery
Zenika 的 JavaScript 顾问兼培训师
非官方测试版翻译

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

升级到新版本的 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 仓库,不会干扰你的项目仓库(无论你使用 Git、SVN、Mercurial 还是其他版本控制系统)。如果发生意外错误,你的源代码会被自动恢复。

工作原理

关键步骤是生成 Git 补丁。这个补丁包含了 React Native 模板从你当前版本到目标版本之间的所有变更。

要获取此补丁,我们需要基于 node_modules 目录中 react-native 包内嵌的模板生成应用(这些模板与 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 能通过三方合并算法自动处理大多数变更,最终仅留下熟悉的冲突标记:

    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 版本启动。若将实现代码嵌入本地 CLI,旧版本用户将无法享受此功能。例如,若新升级代码仅在 0.38.0 发布,则无法从 0.29.2 升级至 0.38.0。

基于 Git 的升级极大改善了开发者体验,使其惠及所有用户至关重要。通过全局安装独立包 react-native-git-upgrade,无论项目使用哪个 React Native 版本,您今天就能使用这项新功能。

另一重要原因是 Martin Konicek 近期完成的 Yeoman 清理工作。我们不愿让 Yeoman 依赖项重新进入 react-native 包,这会影响为创建补丁而进行的旧模板评估。

立即试用并反馈意见

最后我想说:请尽情享受这项功能,欢迎随时提出改进建议、报告问题,特别是提交 PR。每个开发环境存在差异,每个 React Native 项目也各不相同,我们需要您的反馈来确保该功能适用于所有场景。

致谢

衷心感谢杰出企业 ZenikaM6 Web (存档链接),没有它们的支持这一切都不可能实现!

全新 Button 组件、Yarn 加速安装及公开路线图

· 1 分钟阅读
Héctor Ramos
Héctor Ramos
Former Developer Advocate @ Facebook
非官方测试版翻译

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

许多开发者向我们反馈,React Native 的更新迭代速度太快,很难及时追踪最新进展。为更好地同步当前工作重点,我们正式发布了 React Native 开发路线图。整体规划主要围绕三大优先事项展开:

  • 核心库增强:为重点组件和 API 增加更多功能

  • 稳定性提升:改进底层架构以减少错误并提升代码质量

  • 开发者体验优化:帮助 React Native 开发者提高效率

如果您有希望加入路线图的功能建议,欢迎访问 Canny 平台提交新功能提案或参与现有讨论。

React Native 最新动态

今日发布的 React Native 0.37 版本新增了核心按钮组件,可轻松为应用添加可点击按钮。同时引入对新型包管理工具 Yarn 的支持,将大幅加速应用依赖更新流程。

Button 组件正式推出

我们正式推出基础款 <Button /> 组件,完美适配各平台原生视觉风格。这回应了社区最常见的反馈:React Native 是极少数未内置开箱即用按钮的移动开发框架。

Android 与 iOS 平台按钮效果

<Button
onPress={onPressMe}
title="Press Me"
accessibilityLabel="Learn more about this Simple Button"
/>

资深开发者通常这样创建按钮:iOS 使用 TouchableOpacity 实现默认效果,Android 用 TouchableNativeFeedback 实现涟漪反馈,再添加样式修饰。虽然自定义按钮开发门槛不高,但我们致力于让 React Native 学习曲线更平缓。基础按钮组件加入核心库后,新手开发者首日就能实现惊艳功能,无需耗费时间调整按钮样式或学习触摸组件细节。

该组件旨在提供跨平台的原生体验,因此不会包含自定义按钮的所有高级功能。它是绝佳的入门选择,但并非要取代现有按钮方案。欲了解更多详情,请查阅全新 Button 组件文档,内含可运行示例!

使用 Yarn 加速 react-native init

现在您可以使用 JavaScript 新型包管理工具 Yarn 大幅加速 react-native init 过程。请先安装 Yarn 并将 react-native-cli 升级至 1.2.0 版以体验加速效果:

$ npm install -g react-native-cli

新建应用时将显示"Using yarn"提示:

Yarn 使用界面

在优质网络环境下实测显示,react-native init 耗时仅需 1 分钟左右(使用 npm 3.10.8 时约需 3 分钟)。Yarn 安装虽为可选,但我们强烈推荐使用。

致谢

衷心感谢所有为此版本作出贡献的开发者!完整更新日志已在 GitHub 发布。包含二十余项错误修复与新特性的 React Native,因您的付出而持续进化。

0.36:Headless JS、键盘 API 等新特性

· 1 分钟阅读
Héctor Ramos
Héctor Ramos
Former Developer Advocate @ Facebook
非官方测试版翻译

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

今天我们正式发布 React Native 0.36。下面让我们详细了解本次更新的主要内容。

Headless JS

Headless JS 让应用在后台运行时仍能执行 JavaScript 任务,例如同步最新数据、处理推送通知或播放音乐等。目前该功能仅支持 Android 平台。

首先,在独立文件中定义你的异步任务(例如 SomeTaskName.js):

module.exports = async taskData => {
// Perform your task here.
};

接着,在 AppRegistry 中注册你的任务:

AppRegistry.registerHeadlessTask('SomeTaskName', () =>
require('SomeTaskName'),
);

使用 Headless JS 需要编写少量原生 Java 代码来实现按需启动服务。请查阅我们全新的 Headless JS 文档 获取更多信息!

键盘 API

全新的 Keyboard API 让屏幕键盘操作更加便捷。现在你可以监听原生键盘事件并作出响应。例如,要关闭当前键盘只需调用 Keyboard.dismiss()

import {Keyboard} from 'react-native';

// Hide that keyboard!
Keyboard.dismiss();

动画除法运算

React Native 原本已支持通过加法、乘法和取模运算组合两个动画值。在 0.36 版本中,现在可以通过除法运算组合动画值。当某个动画值需要基于另一个动画值的倒数进行计算时(例如缩放反转:2x → 0.5x),这就非常有用:

const a = Animated.Value(1);
const b = Animated.divide(1, a);

Animated.spring(a, {
toValue: 2,
}).start();

此时 b 将跟随 a 的弹性动画变化,并输出 1 / a 的计算结果。

基础用法示例如下:

<Animated.View style={{transform: [{scale: a}]}}>
<Animated.Image style={{transform: [{scale: b}]}} />
<Animated.View>

在此示例中,内部图片完全不会产生拉伸,因为父级缩放效果被完全抵消。如需了解更多,请查阅 动画指南

深色状态栏

StatusBar 新增了 barStyle 属性值:dark-content。现在你可以在 Android 和 iOS 平台统一使用 barStyle 属性,具体效果如下:

  • default:使用平台默认样式(iOS 浅色,Android 深色)

  • light-content:浅色状态栏配黑色文字与图标

  • dark-content:深色状态栏配白色文字与图标

其他更新

以上仅是 0.36 版本的部分更新内容。请查看 GitHub 发布说明 了解完整的新特性、问题修复和重大变更清单。

你可以通过在终端运行以下命令升级到 0.36 版本:

$ npm install --save react-native@0.36
$ react-native upgrade