跳至主内容

为 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 支持能帮助您拓展国际用户群体!

旧金山见面会回顾

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

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

上周我有幸参加了在 Zynga 旧金山办公室举办的 React Native 见面会。现场约有 200 人参加,这是一个绝佳的机会,让我结识了附近同样对 React Native 感兴趣的其他开发者。

我特别想了解 Zynga、Netflix 和 Airbnb 等公司如何使用 React 和 React Native。当晚的议程安排如下:

  • React 中的快速原型设计

  • 为 React Native 设计 API

  • 弥合鸿沟:在现有代码库中使用 React Native

不过活动开始前,我们先进行了简短介绍和近期动态回顾:

  • 你知道吗?React Native 已是 GitHub 上最热门的 Java 仓库

  • rnpm 现已并入 React Native 核心!现在你可以使用 react-native link 替代 rnpm link安装带原生依赖的库

  • React Native 见面会社区正在快速壮大!目前全球各地的聚会小组已有超过 4,800 名开发者参与

如果这些聚会在你附近举办,我强烈建议参加!

Zynga 的 React 快速原型实践

新闻分享环节后,当晚的东道主 Zynga 进行了简短介绍。Abhishek Chadha 分享了他们如何利用 React 在移动端快速原型设计新体验,并演示了一款类 Draw Something 应用的快速原型。他们采用与 React Native 类似的方法,通过桥接机制访问原生 API。当 Abhishek 调用设备摄像头拍摄观众照片并在某人头上绘制帽子时,这一点得到了生动展示。

Netflix 的 React Native API 设计之道

接下来是当晚的首场主题演讲。Netflix 高级软件工程师 Clarence Leung 分享了为 React Native 设计 API 的经验。他首先指出开发者可能构建的两类库:标签栏/日期选择器等组件库,以及相机相册/应用内支付等原生服务访问库。在构建 React Native 库时,主要有两种实现路径:

  • 提供平台特定组件

  • 构建跨平台库(Android/iOS 保持相似 API)

每种方案各有考量,具体选择取决于你的实际需求。

方案一

Clarence 以 React Native 核心的 DatePickerIOS 和 DatePickerAndroid 为例说明平台特定组件。iOS 的日期选择器作为 UI 元素直接嵌入视图,而 Android 则以模态框形式呈现。这种情况下提供独立组件是合理的设计选择。

方案二

另一方面,照片选择器在 Android 和 iOS 上的处理方式类似。存在一些细微差异——例如 Android 不会像 iOS 那样将照片分组到"自拍"等文件夹中——但这些差异可以通过 if 语句和 Platform 组件轻松处理。

无论您选择哪种方法,最小化 API 表面并构建特定于应用的库都是明智之举。例如,iOS 的应用内购买框架支持一次性消耗性购买以及可续订订阅。如果您的应用仅需支持消耗性购买,您可以在跨平台库中直接省略订阅功能。

Clarence 演讲结束后进行了简短的问答环节。其中透露的一个有趣细节是:Netflix 为这些库编写的 React Native 代码中,约 80% 可在 Android 和 iOS 平台共享。

弥合差距:在现有代码库中使用 React Native

当晚的最后一场演讲来自 Airbnb 的 Leland Richardson,聚焦于在现有代码库中使用 React Native。我深知使用 React Native 从零开发新应用非常便捷,因此对 Airbnb 在现有原生应用中集成 React Native 的经验格外感兴趣。

Leland 首先介绍了 greenfield 应用与 brownfield 应用的区别。Greenfield 指无需考虑历史包袱的全新项目,而 brownfield 项目则需要兼顾现有工程需求、开发流程及多团队协作等复杂因素。

开发 greenfield 应用时,React Native CLI 会为 Android 和 iOS 创建统一仓库。Airbnb 面临的第一个挑战是:他们的 Android 和 iOS 应用分属独立仓库。采用多仓库结构的公司在引入 React Native 前需跨越这一障碍。

为此,Airbnb 首先为 React Native 代码建立了专属仓库。他们通过持续集成服务器将 Android/iOS 仓库镜像至此新仓库,测试运行并构建 bundle 后,再将产物同步回原仓库。这样移动工程师可在不改变开发环境的前提下处理原生代码——无需安装 npm、运行打包服务或手动构建 JavaScript bundle。而编写 React Native 的工程师则直接在专属仓库工作,无需操心跨平台代码同步。

该方案存在明显缺陷:无法实现原子更新。涉及原生和 JavaScript 联动的变更需拆分成三个独立 PR,且必须谨慎协调合并顺序。为避免冲突,若主分支在构建期间发生变化,CI 将中止向 Android/iOS 仓库的同步。这在高频提交时期(如版本发布阶段)会导致严重延迟。

Airbnb 现已转向单仓库方案。值得庆幸的是该方案已在规划中,当 Android/iOS 团队熟悉 React Native 工作流后,便欣然推动了单仓库迁移进程。

这基本解决了分仓库方案的问题。Leland 也指出单仓库会给版本控制系统带来更大压力,小型公司需评估此影响。

导航难题

Leland 演讲的后半段聚焦于 React Native 的核心痛点:导航解决方案。他剖析了丰富的导航库生态(包括官方和第三方方案),特别提到 NavigationExperimental 看似前景广阔,最终却未能满足其实际需求。

实际上,现有的导航库似乎都无法很好地支持遗留应用(brownfield app)。这类应用要求导航状态完全由原生应用掌控。例如,当用户会话在 React Native 视图展示期间过期时,原生应用应能随时接管并弹出登录界面。

Airbnb 还希望避免在过渡期间用 JavaScript 实现的导航栏替换原生导航栏,因为这种切换可能产生割裂感。最初他们仅限模态视图使用 React Native,但这显然限制了 React Native 在整个应用中的推广范围。

因此他们决定开发专属库 airbnb-navigation。由于该库深度耦合 Airbnb 的代码库,目前尚未开源,但他们计划在今年年底前发布。

此处不深入解析库的 API 设计,但核心要点包括:

  • 必须预先注册所有场景(scene)

  • 每个场景都在独立的 RCTRootView 中渲染,并通过各平台原生方式呈现(例如 iOS 使用 UINavigationController

  • 场景中的主 ScrollView 需包裹在 ScrollScene 组件内,从而支持点击状态栏自动回滚等原生行为

  • 场景转场由原生系统处理,无需担心性能损耗

  • 自动支持 Android 返回键操作

  • 通过无界面的 Navigator.Config 组件实现基于视图控制器的导航栏样式配置

同时也需注意以下限制:

  • 导航栏作为原生组件难以通过 JavaScript 深度定制(这是该库的硬性要求)

  • 通过桥接传递的 ScreenProps 需序列化/反序列化,传递大量数据时需谨慎

  • 导航状态由原生应用管控(核心设计原则),因此 Redux 等方案无法操作导航状态

演讲后的问答环节中,Airbnb 表示对 React Native 整体满意。他们计划采用 Code Push 实现热修复以绕过应用商店审核,工程师们尤其推崇 Live Reload 功能——无需重新编译原生应用即可实时查看代码改动。

活动尾声

活动结束时分享了更多 React Native 动态:

技术沙龙是向社区开发者学习交流的绝佳机会。期待未来参与更多 React Native 线下聚会。若您也参加相关活动,欢迎随时交流探讨如何让 React Native 更好地服务您的开发需求!

迈向更完善的文档

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

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

卓越的开发者体验离不开卓越的文档支持。编写优质文档需要付出诸多努力——理想的文档应具备简洁性、实用性、准确性、完整性和愉悦性。近期我们根据您的反馈持续优化文档,现与您分享部分改进成果。

内联示例

当您学习新库、新编程语言或新框架时,总会有个美妙瞬间:初次编写代码片段,尝试运行,观察结果...然后它_真的_运行成功了!您亲手创造了实际成果。我们致力于将这种沉浸式体验融入文档中,例如:

import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';

class ScratchPad extends Component {
render() {
return (
<View style={{flex: 1}}>
<Text style={{fontSize: 30, flex: 1, textAlign: 'center'}}>
Isn't this cool?
</Text>
<Text style={{fontSize: 100, flex: 1, textAlign: 'center'}}>
👍
</Text>
</View>
);
}
}

AppRegistry.registerComponent('ScratchPad', () => ScratchPad);

我们相信这些内联示例(借助 Devin Abbott 开发的 react-native-web-player 模块)是学习 React Native 基础的最佳途径。为此我们更新了面向新手的 React Native 教程,尽可能采用此类示例。欢迎体验——如果您曾好奇修改示例代码会产生什么变化,这将是绝佳的探索方式。此外,若您正在开发工具并希望在自有站点展示实时 React Native 示例,react-native-web-player 能让此过程变得简单直接。

核心模拟引擎由 Nicolas Gallagherreact-native-web 项目提供,该项目支持在 Web 端展示 TextView 等 React Native 组件。如果您希望构建共享大量代码的移动端与 Web 端应用,请关注 react-native-web

优化指南

React Native 某些功能存在多种实现方式,我们收到反馈建议需要更明确的指导。

全新推出的导航指南对比了不同方案(NavigatorNavigatorIOSNavigationExperimental)并提供选用建议。中期规划中我们将改进并整合这些接口,短期目标则是通过优化指南减轻您的决策负担。

我们还新增了触摸事件处理指南,讲解创建类按钮界面的基础知识,并简要归纳了处理触摸事件的不同方式。

另一优化重点是 Flexbox 布局,包含使用 Flexbox 处理布局控制组件尺寸的教程,以及虽不炫酷但极具实用价值的React Native 布局控制属性全集

入门指引

在本地搭建 React Native 开发环境时,您需要完成大量安装配置工作。虽然难以将安装过程变得趣味十足,但我们至少能使其尽可能快速无痛。

我们构建了新版入门流程,支持预先选择开发操作系统与移动操作系统,将所有设置说明整合至统一界面。我们完整走查安装流程以确保各环节可靠运行,并为每个决策点提供明确建议。经过同事实测验证,我们确信这是项显著改进。

我们还在将 React Native 集成到现有应用的指南上做了改进。许多使用 React Native 的大型应用(包括 Facebook 应用自身)实际上都是部分采用 React Native 构建,部分使用常规开发工具完成的。我们希望这份指南能让更多人轻松采用这种开发模式。

我们需要您的帮助

您的反馈决定了我们的工作优先级。我知道有人读完这篇博文会想:"文档改进?切!X 功能的文档还是那么烂!"——这种态度很棒,我们需要这种能量。根据反馈类型的不同,最佳提交方式也有所区别:

如果发现文档中的具体错误(如描述不准确或代码无法运行),请提交 issue。务必添加"Documentation"标签,方便我们快速分配给对应负责人。

如果没有具体错误,但某些文档内容存在根本性困惑,则不适合提交 GitHub issue。请到 Canny 平台说明需要改进的文档区域。这能帮助我们在编写指南等系统性工作时合理规划优先级。

感谢您阅读至此,也感谢您使用 React Native!

React Native:一年回顾

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

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

自我们将 React Native 开源以来,已经过去了一年。当初只是几位工程师的构想,如今已成为 Facebook 内外产品团队广泛使用的框架。今天在 F8 大会上,我们宣布微软正将 React Native 引入 Windows 生态,让开发者能够在 Windows PC、Phone 和 Xbox 上构建 React Native 应用。微软还将提供 Visual Studio Code 的 React Native 扩展和 CodePush 等开源工具与服务,帮助开发者在 Windows 平台上创建 React Native 应用。此外,三星正在为其混合平台构建 React Native,使开发者能够为数百万台 SmartTV 及移动和可穿戴设备开发应用。我们还发布了 Facebook SDK for React Native,让开发者能更轻松地将登录、分享、应用分析和 Graph API 等 Facebook 社交功能集成到应用中。短短一年间,React Native 已经改变了开发者在各大主流平台的构建方式。

这是一段激动人心的旅程——而我们才刚刚启航。在此,我们将回顾 React Native 自一年前开源以来的成长与演变,分享我们途中遇到的挑战,并展望未来的发展方向。

本文为节选。阅读完整文章请访问 Facebook Code

深入探索 React Native 性能优化

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

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

React Native 让您能够使用 JavaScript 构建 Android 和 iOS 应用,借助 React 和 Relay 的声明式编程模型。这种模式带来了更简洁易懂的代码、无需编译周期的快速迭代能力,以及跨平台代码的轻松复用。您可以更快地交付产品,专注于真正重要的细节,让应用呈现出令人惊艳的视觉效果和交互体验。性能优化正是其中关键一环。本文将讲述我们如何使 React Native 应用启动速度提升两倍的故事。

为何追求极致速度?

更快的应用意味着内容加载更迅速,用户有更多时间进行交互;流畅的动画则能带来愉悦的使用体验。在新兴市场,2011 年款手机搭配2G 网络仍是主流,此时性能优化往往决定着应用能否真正投入使用。

自从在iOSAndroid平台发布 React Native 以来,我们持续优化了列表滚动性能、内存效率、UI 响应速度以及应用启动时间。启动速度决定了用户对应用的第一印象,同时考验着框架的各个组成部分,因此这是最具挑战性也最值得投入的优化方向。

本文为节选,完整内容请阅读 Facebook Code 博客

热重载功能正式发布

· 1 分钟阅读
Martín Bigio
Instagram 软件工程师
非官方测试版翻译

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

React Native 的核心目标是提供最佳的开发者体验。其中关键环节是从保存文件到看到变更所需的时间。我们的目标是将这个反馈循环压缩到 1 秒以内,即使应用规模持续增长。

我们通过三大特性逼近这个理想目标:

  • 采用 JavaScript 语言,避免冗长的编译周期

  • 实现名为 Packager 的工具,将 ES6/Flow/JSX 文件转换为虚拟机可理解的常规 JavaScript。该工具设计为服务架构,在内存中保存中间状态以实现快速增量更新,并充分利用多核性能

  • 构建 Live Reload 功能,实现保存时自动重载应用

至此,开发者的瓶颈不再是重载应用所需时间,而是应用状态的丢失。典型场景是:当你在距离启动屏多个页面之外的模块上工作时,每次重载都必须重复点击相同路径才能返回工作区,导致开发循环延长至数秒

热重载

热重载的核心原理是保持应用运行状态,在运行时注入已编辑文件的新版本。这样就不会丢失任何状态,特别适合调整 UI 元素

百闻不如一见,请对比 Live Reload(现有)与 Hot Reload(全新)的实际效果:

仔细观察会发现:系统能从红屏错误中自动恢复,还能直接导入新增模块而无需完全重载

重要提示: 由于 JavaScript 是强状态语言,热重载无法完美实现。实践中我们发现当前方案能覆盖大多数常规场景,若出现异常随时可通过完全重载恢复

热重载功能自 0.22 版本起可用,启用方式:

  • 打开开发者菜单

  • 点击"启用热重载"

实现原理简析

了解其价值和使用方法后,现在进入精彩环节:运作原理揭秘

热重载基于 模块热替换(HMR)功能构建。该技术由 webpack 首创,我们在 React Native Packager 中实现了它。HMR 让 Packager 监听文件变更,并向应用内嵌的轻量 HMR 运行时发送更新

简言之,HMR 更新包含变更 JS 模块的新代码。运行时接收后会将旧模块代码替换为新版本:

HMR 更新的内容不仅包含目标模块代码,因为单纯替换代码不足以让运行时感知变更。问题在于模块系统可能已缓存目标模块的_导出值_。例如某个应用包含两个模块:

// log.js
function log(message) {
const time = require('./time');
console.log(`[${time()}] ${message}`);
}

module.exports = log;
// time.js
function time() {
return new Date().getTime();
}

module.exports = time;

log 模块负责输出带时间戳的消息,时间数据由 time 模块提供

应用打包时,React Native 通过 __d 函数在模块系统中注册各模块。对于此应用,在众多 __d 定义中会有针对 log 的注册:

__d('log', function() {
... // module's code
});

该调用将每个模块的代码包裹在一个匿名函数中,我们通常称之为工厂函数。模块系统运行时负责跟踪每个模块的工厂函数、是否已执行以及执行结果(导出值)。当模块被引入时,模块系统要么提供已缓存的导出值,要么首次执行该模块的工厂函数并保存结果。

假设您启动应用并引入 log 模块。此时 logtime 的工厂函数均未执行,因此没有导出值被缓存。接着用户修改 time 模块使其返回 MM/DD 格式的日期:

// time.js
function bar() {
const date = new Date();
return `${date.getMonth() + 1}/${date.getDate()}`;
}

module.exports = bar;

Packager 会将 time 的新代码发送至运行时(步骤1)。当 log 最终被引入时,其导出的函数会执行并使用 time 的修改(步骤2):

现在假设 log 的代码在顶层引入了 time

const time = require('./time'); // top level require

// log.js
function log(message) {
console.log(`[${time()}] ${message}`);
}

module.exports = log;

log 被引入时,运行时将缓存其导出值及 time 的导出值(步骤1)。若此时修改了 time 模块,热更新流程不能仅替换完 time 的代码就结束。否则当 log 执行时,它仍会使用缓存的旧版 time 代码。

为了让 log 获取 time 的变更,我们需要清除其缓存的导出值(步骤3)。最后当 log 再次被引入时,其工厂函数将重新执行,此时会引入新版 time 代码。

HMR API

React Native 的 HMR 通过引入 hot 对象扩展了模块系统。该 API 基于 webpack 的实现。hot 对象暴露的 accept 函数允许您定义回调,在模块需要热替换时执行。例如,若将 time 代码修改如下,每次保存时控制台将显示 "time changed":

// time.js
function time() {
... // new code
}

module.hot.accept(() => {
console.log('time changed');
});

module.exports = time;

请注意,仅在极少数情况下需要手动使用此 API。热重载功能在大多数常见场景中均可开箱即用。

HMR 运行时

如前所述,有时仅接受 HMR 更新并不足够——因为使用被热替换模块的其他模块可能已被执行且其导入值已缓存。例如,假设电影应用示例的依赖树中,顶层 MovieRouter 依赖于 MovieSearchMovieScreen 视图,而这两个视图又依赖于前例中的 logtime 模块:

若用户访问了电影搜索视图但未访问其他视图,除 MovieScreen 外所有模块的导出值都会被缓存。当 time 模块变更时,运行时必须先清除 log 的导出值才能使其获取 time 的变更。流程不会就此结束:运行时会递归向上处理所有父模块直至被完全接受。因此它会找到依赖 log 的模块并尝试接受更新:对于未加载的 MovieScreen 可跳过;对于 MovieSearch 则需清除其导出值并递归处理父模块;最终同样处理 MovieRouter 并因无上级依赖而终止。

为遍历依赖树,运行时通过 HMR 更新从 Packager 获取反向依赖树。此例中运行时将收到如下 JSON 对象:

{
modules: [
{
name: 'time',
code: /* time's new code */
}
],
inverseDependencies: {
MovieRouter: [],
MovieScreen: ['MovieRouter'],
MovieSearch: ['MovieRouter'],
log: ['MovieScreen', 'MovieSearch'],
time: ['log'],
}
}

React 组件

要让 React 组件实现热重载会稍显棘手。问题在于如果直接替换新旧代码,我们会丢失组件状态。针对 React Web 应用,Dan Abramov 开发了一套 Babel 转换器,利用 webpack 的 HMR API 解决了这个问题。简言之,他的方案通过在转换阶段为每个 React 组件创建代理来实现。这些代理组件负责保存状态,并将生命周期方法委托给实际组件(即热重载的目标组件):

除了创建代理组件,该转换器还定义了包含强制 React 重新渲染组件的 accept 函数。这样我们就能在不丢失应用状态的前提下热更新渲染代码。

React Native 自带的默认转换器使用 babel-preset-react-native,其配置方式与你在基于 webpack 的 React Web 项目中使用 react-transform 完全一致。

Redux 状态管理

要为 Redux 启用热重载,操作方式与基于 webpack 的 Web 项目类似:

// configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from '../reducers';

export default function configureStore(initialState) {
const store = createStore(
reducer,
initialState,
applyMiddleware(thunk),
);

if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../reducers/index').default;
store.replaceReducer(nextRootReducer);
});
}

return store;
};

当你修改 reducer 时,接受该 reducer 的代码将被发送到客户端。客户端会识别到 reducer 无法自我更新,于是查找所有引用它的模块并尝试更新。最终流程会到达唯一的 store —— 即 configureStore 模块,它将接受 HMR 更新。

结语

如果你有兴趣帮助改进热重载功能,推荐阅读 Dan Abramov 关于热重载未来的文章并参与贡献。例如 Johny Days 正在实现多客户端同步热重载。这项功能的维护与优化需要大家共同参与。

通过 React Native,我们得以重新思考应用构建方式以优化开发体验。热重载只是其中一环,还能通过哪些创新方案让体验更上一层楼?

构建可访问的 React Native 应用

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

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

随着近期 React 在 Web 端的发布和 React Native 在移动端的推出,我们为开发者提供了构建产品的新前端框架。打造健壮产品的关键要素是确保所有人都能使用它,包括视力障碍或其他残疾人士。React 和 React Native 的可访问性 API 能让你构建的应用体验适用于使用辅助技术的用户,例如为盲人和视障人士设计的屏幕阅读器。

本文将重点讨论 React Native 应用。我们将 React 可访问性 API 设计得与 Android 和 iOS 原生 API 的体验保持一致。如果你曾为 Android、iOS 或 Web 开发过无障碍应用,应该会对 React AX API 的框架和术语体系感到熟悉。例如,你可以让 UI 元素变得 accessible(从而暴露给辅助技术),并通过 accessibilityLabel 为元素提供文本描述:

<View accessible={true} accessibilityLabel=”This is simple view”>

让我们通过 Facebook 自家的一款 React 产品 广告管理应用,来深入探索 React AX API 的实际应用。

本文为节选内容,完整文章请阅读 Facebook Code

React Native for Android:我们如何构建第一个跨平台 React Native 应用

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

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

今年早些时候,我们推出了 React Native for iOS。React Native 将开发者们在 Web 上熟悉的 React 特性——声明式自包含 UI 组件和快速开发周期——带到了移动平台,同时保留了原生应用的速度、保真度和体验。今天,我们很高兴发布 React Native for Android。

在 Facebook,我们在生产环境中使用 React Native 已经超过一年。差不多正好一年前,我们的团队着手开发 Ads Manager 应用。我们的目标是创建一个新应用,让数百万在 Facebook 上投放广告的用户能够随时随地管理账户并创建新广告。最终,它不仅是 Facebook 第一个完全使用 React Native 构建的应用,也是第一个跨平台应用。在本文中,我们将分享我们如何构建这个应用,React Native 如何让我们更快地推进,以及我们吸取的经验教训。

本文为节选。阅读完整文章请访问 Facebook Code

React Native:将现代 Web 技术引入移动端

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

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

两年前我们将 React 推向世界,此后它在 Facebook 内外都取得了令人瞩目的发展。如今,即使没有人被强制使用它,Facebook 的新 Web 项目通常都以某种形式采用 React 构建,整个行业也都在广泛接纳这项技术。工程师们每天选择使用 React,因为它能让他们更专注于产品本身,而非与框架作斗争。然而直到我们使用 React 开发一段时间后,才开始真正理解其强大之处。

React 要求我们将应用拆分为离散的组件,每个组件代表单一视图。这些组件让产品迭代更轻松——因为修改局部功能时无需在脑中构建整个系统。更重要的是,React 用声明式 API 封装了 DOM 的易变命令式接口,提升了抽象层级并简化了编程模型。我们发现使用 React 构建时,代码可预测性大幅提高。这种可预测性让我们能自信地快速迭代,应用也因此更加可靠。此外,不仅基于 React 的应用更易扩展,我们还发现团队规模也更易扩展。

凭借 Web 的快速迭代周期,我们已用 React 构建了诸多出色产品,包括 Facebook.com 的许多组件。同时我们还在 React 基础上构建了 Relay 等卓越的 JavaScript 框架,极大简化了大规模数据获取。当然,Web 只是故事的一部分。Facebook 还拥有基于割裂的专有技术栈构建的 Android 和 iOS 应用。多平台开发不仅分裂了我们的工程组织,也仅是原生移动应用开发困境的冰山一角。

本文为节选。请访问 Facebook Code 阅读全文。