跳至主要内容

0.36:无头 JS,键盘 API 等等

·阅读时间:3 分钟
Héctor Ramos
Héctor Ramos
Facebook 前开发者布道师

今天我们发布了 React Native 0.36。继续阅读以了解有关新增功能的更多信息。

无头 JS

无头 JS 是一种在您的应用处于后台时运行 JavaScript 任务的方法。例如,它可用于同步新数据、处理推送通知或播放音乐。目前仅在 Android 上可用。

要开始使用,请在专用文件中定义您的异步任务(例如 SomeTaskName.js

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

接下来,在 AppRegistry 上注册您的任务

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

使用无头 JS 确实需要编写一些原生 Java 代码,以便在需要时启动服务。查看我们新的 无头 JS 文档 了解更多信息!

键盘 API

使用屏幕键盘现在更容易了 Keyboard。您现在可以监听原生键盘事件并对其做出反应。例如,要关闭活动键盘,只需调用 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>

在此示例中,内部图像根本不会被拉伸,因为父级的缩放被抵消了。如果您想了解更多信息,请查看 动画指南

深色状态栏

一个新的 barStyle 值已添加到 StatusBardark-content。有了这个新增功能,您现在可以在 Android 和 iOS 上都使用 barStyle。行为现在如下

  • default:使用平台默认值(iOS 上为浅色,Android 上为深色)。
  • light-content:使用浅色状态栏,黑色文字和图标。
  • dark-content:使用深色状态栏,白色文字和图标。

…等等

以上只是 0.36 中更改内容的示例。查看 GitHub 上的发布说明 以查看完整的新功能、错误修复和重大更改列表。

您可以通过在终端中运行以下命令升级到 0.36

$ npm install --save [email protected]
$ react-native upgrade

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

·阅读时间:7 分钟
Mengjue (Mandy) Wang
Facebook 软件工程师实习生

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

我们很高兴地宣布,React Native 已得到改进以支持 RTL 布局。此功能现已在 react-native master 分支中提供,并将包含在下一个 RC 版本中:v0.33.0-rc

这涉及更改 css-layout(RN 使用的核心布局引擎)和 RN 核心实现,以及支持 RTL 的特定 OSS JS 组件。

为了在生产环境中测试 RTL 支持,最新版本的 Facebook Ads Manager 应用(第一个 100% 使用 RN 的跨平台应用)现在已提供阿拉伯语和希伯来语版本,并为 iOSAndroid 提供 RTL 布局。以下是在这些 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 语言包添加到您的应用中。

    • 请参阅来自 iOSAndroid 的通用指南。
  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,以便您可以根据布局进行必要的更改。

具有方向含义的图标

如果您的组件包含图标或图像,它们将在 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,例如 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 支持应该涵盖您应用中的大部分 UX;但是,目前存在一些限制

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

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

  • 使手势和动画的 RTL 支持更易于开发者使用

    目前,要使手势和动画与 RTL 兼容,仍然需要一些编程工作。将来,理想情况下,需要找到一种方法,使手势和动画的 RTL 支持更易于开发者使用。

试试看!

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

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

旧金山聚会回顾

·阅读时间:9 分钟
Héctor Ramos
Héctor Ramos
Facebook 前开发者布道师

上周我有机会参加了在 Zynga 旧金山办公室举行的 React Native 聚会。大约有 200 人参加,这是一个与我附近其他对 React Native 感兴趣的开发者见面的好地方。

我特别感兴趣的是了解像 Zynga、Netflix 和 Airbnb 这样的公司如何使用 React 和 React Native。当晚的议程如下

  • React 中的快速原型设计
  • 为 React Native 设计 API
  • 弥合差距:在现有代码库中使用 React Native

但首先,活动以简短的介绍和最近新闻的简要回顾开始

如果 其中一个聚会 在您附近举行,我强烈建议您参加!

Zynga 的 React 快速原型设计

第一轮新闻之后,Zynga(我们当晚的东道主)进行了简短的介绍。Abhishek Chadha 谈到了他们如何使用 React 在移动设备上快速创建新体验的原型,并演示了一个类似于 Draw Something 应用的快速原型。他们使用与 React Native 类似的方法,通过桥接提供对原生 API 的访问。当 Abhishek 使用设备的摄像头拍摄观众的照片,然后在某人的头上画一顶帽子时,这一点得到了证明。

Netflix 的 React Native API 设计

接下来,是当晚的第一个特色演讲。Clarence Leung(Netflix 的高级软件工程师)发表了关于为 React Native 设计 API 的演讲。首先,他指出了人们可能会处理的两种主要类型的库:组件(如选项卡栏和日期选择器)以及提供对原生服务(如相机胶卷或应用内支付)访问的库。在构建供 React Native 使用的库时,有两种方法可以采用

  • 提供特定于平台的组件
  • 一个跨平台库,在 Android 和 iOS 上具有类似的 API

每种方法都有自己的考虑因素,由您决定哪种方法最适合您的需求。

方法 #1

作为特定于平台的组件的一个示例,Clarence 谈到了核心 React Native 中的 DatePickerIOS 和 DatePickerAndroid。在 iOS 上,日期选择器作为 UI 的一部分呈现,可以轻松地嵌入到现有视图中,而在 Android 上,日期选择器以模态方式呈现。在这种情况下,提供单独的组件是有意义的。

方法 #2

另一方面,照片选择器在 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 项目形成对比,在 Brownfield 项目中,您需要考虑现有项目的需求、开发流程以及所有团队的各种需求。

在开发 Greenfield 应用时,React Native CLI 会为 Android 和 iOS 设置一个单一的代码库,并且一切都能正常工作。在 Airbnb 使用 React Native 面临的第一个挑战是,Android 和 iOS 应用分别拥有自己的代码库。多代码库的公司在采用 React Native 之前需要克服一些障碍。

为了解决这个问题,Airbnb 首先为 React Native 代码库设置了一个新的代码库。他们使用其持续集成服务器将 Android 和 iOS 代码库镜像到这个新的代码库中。在运行测试并构建 bundle 后,构建工件将同步回 Android 和 iOS 代码库。这使得移动工程师能够在不改变其开发环境的情况下处理原生代码。移动工程师无需安装 npm、运行打包器或记住构建 JavaScript bundle。编写实际 React Native 代码的工程师无需担心跨 Android 和 iOS 同步代码,因为他们直接在 React Native 代码库上工作。

这确实带来了一些缺点,主要是在他们无法进行原子更新。需要结合原生和 JavaScript 代码的更改需要三个单独的 pull request,所有这些都需要仔细地合并。为了避免冲突,如果在构建开始后 master 发生了更改,CI 将无法将更改合并回 Android 和 iOS 代码库。这会导致在提交频率很高(例如在发布新版本时)的日子里出现长时间延迟。

Airbnb 后来转向了单代码库方法。幸运的是,这已经正在考虑之中,并且一旦 Android 和 iOS 团队对使用 React Native 感到满意,他们很乐意加速向单代码库的迁移。

这解决了他们使用分离代码库方法遇到的大多数问题。Leland 确实指出,这会对版本控制服务器造成更大的压力,这对较小的公司来说可能是一个问题。

导航问题

Leland 演讲的后半部分重点关注了一个我非常关心的主题:React Native 中的导航问题。他谈到了 React Native 中大量存在的导航库,包括第一方和第三方库。NavigationExperimental 被认为很有希望,但最终并不适合他们的用例。

事实上,没有一个现有的导航库似乎适用于 Brownfield 应用。Brownfield 应用要求导航状态完全由原生应用拥有。例如,如果用户会话在呈现 React Native 视图时过期,则原生应用应该能够接管并根据需要呈现登录屏幕。

Airbnb 还希望避免在过渡过程中用 JavaScript 版本替换原生导航栏,因为这样可能会导致突兀的效果。最初,他们将自己限制在模态呈现的视图中,但当要在其应用中更广泛地采用 React Native 时,这显然会出现问题。

他们决定需要自己的库。该库名为 airbnb-navigation。该库尚未开源,因为它与 Airbnb 的代码库紧密相关,但他们希望在今年年底发布它。

我不会详细介绍该库的 API,但以下是一些关键要点:

  • 必须提前预注册场景。
  • 每个场景都在其自己的 RCTRootView 中显示。它们在每个平台上以原生方式呈现(例如,在 iOS 上使用 UINavigationController)。
  • 场景中的主要 ScrollView 应包装在 ScrollScene 组件中。这样做可以让您利用原生的行为,例如在 iOS 上点击状态栏滚动到顶部。
  • 场景之间的过渡以原生方式处理,无需担心性能。
  • Android 返回按钮自动支持。
  • 他们可以通过 Navigator.Config 无 UI 组件利用基于 View Controller 的导航栏样式。

还有一些需要考虑的事项:

  • 导航栏在 JavaScript 中不容易自定义,因为它是一个原生组件。这是有意的,因为使用原生导航栏是这种类型的库的硬性要求。
  • 每当 ScreenProps 通过桥接传递时,都需要进行序列化/反序列化,因此如果在此处发送太多数据,则必须小心。
  • 导航状态由原生应用拥有(也是该库的硬性要求),因此 Redux 等内容无法操作导航状态。

Leland 的演讲之后也进行了一个问答环节。总的来说,Airbnb 对 React Native 感到满意。他们有兴趣使用 Code Push 修复任何问题,而无需通过 App Store,并且他们的工程师喜欢 Live Reload,因为他们不必在每次进行微小更改后等待原生应用重新构建。

结束语

活动以一些额外的 React Native 新闻结束。

Meetup 提供了一个与社区中的其他开发者见面和学习的好机会。我期待着将来参加更多 React Native Meetup。如果您参加其中一个 Meetup,请留意我,并告诉我我们如何才能让 React Native 更好地为您服务!

迈向更好的文档

·阅读时间:4 分钟
Kevin Lacker
Facebook 工程经理

拥有良好的开发者体验的一部分是拥有良好的文档。创建良好的文档需要付出很多努力 - 理想的文档应简洁、实用、准确、完整且令人愉悦。最近,我们一直在努力根据您的反馈改进文档,并且我们想分享我们所做的一些改进。

内联示例

当您学习新的库、新的编程语言或新的框架时,有一个美好的时刻,当您第一次编写一些代码,尝试一下,看看它是否有效……并且它确实有效。您创造了一些真实的东西。我们希望将这种直观的体验直接融入我们的文档中。像这样:

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 上显示 React Native 组件(如 TextView)的方法。如果您有兴趣构建共享大量代码库的移动和 Web 体验,请查看 react-native-web

改进的指南

在 React Native 的某些部分,有多种方法可以完成任务,我们已经收到反馈,认为我们可以提供更好的指导。

我们有一个新的 导航指南,它比较了不同的方法并建议您应该使用什么 - NavigatorNavigatorIOSNavigationExperimental。从中期来看,我们正在努力改进和整合这些接口。从短期来看,我们希望更好的指南能够让您的生活更轻松。

我们还有一个新的 处理触摸指南,它解释了创建按钮式界面的一些基础知识,以及处理触摸事件的不同方法的简要概述。

我们改进的另一个领域是 Flexbox。这包括有关如何 使用 Flexbox 处理布局 以及如何控制 组件大小 的教程。它还包括一个不那么吸引人但希望有用的 React Native 中控制布局的所有 props 列表

入门

当您开始在您的机器上设置 React Native 开发环境时,您确实需要进行大量安装和配置操作。很难让安装成为一次真正有趣和令人兴奋的体验,但我们至少可以使其尽可能快速和无痛。

我们构建了一个 新的入门工作流程,它允许您提前选择您的开发操作系统和移动操作系统,以便提供一个包含所有设置说明的简洁位置。我们还经历了安装过程以确保一切正常,并确保每个决策点都有明确的建议。在对我们无辜的同事进行测试后,我们非常确定这是一个改进。

我们还致力于改进 将 React Native 集成到现有应用的指南。许多使用 React Native 的大型应用,例如 Facebook 应用本身,实际上都是部分使用 React Native 构建,部分使用常规开发工具构建的。我们希望这份指南能帮助更多人以这种方式构建应用。

我们需要您的帮助

您的反馈让我们知道应该优先处理什么。我知道有些人读完这篇博文后会想:“更好的文档?切。X 的文档仍然很糟糕!”。这很好 - 我们需要这样的活力。提供反馈的最佳方式取决于反馈的类型。

如果您在文档中发现错误,例如不准确的描述或无法正常工作的代码,请 提交问题。请将其标记为“文档”,以便更容易将其路由到合适的人员。

如果文档中没有具体的错误,但某些内容从根本上令人困惑,那么它不适合作为 GitHub 问题。相反,请在 Canny 上发布有关需要改进的文档部分的信息。这有助于我们在进行指南编写等更普遍的工作时确定优先级。

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

React Native:一年的回顾

·阅读时间:2分钟
Martin Konicek
Facebook 软件工程师

React Native 开源已经一年了。从最初少数工程师的一个想法,到现在已成为 Facebook 及其他公司产品团队都在使用的框架。今天在 F8 大会上,我们宣布微软将 将 React Native 带到 Windows 生态系统,使开发人员能够在 Windows PC、手机和 Xbox 上构建 React Native 应用。它还将提供开源工具和服务,例如 Visual Studio Code 的 React Native 扩展和 CodePush,以帮助开发人员在 Windows 平台上创建 React Native 应用。此外,三星 正在为其混合平台构建 React Native,这将使开发人员能够为数百万台智能电视以及移动和可穿戴设备构建应用。我们还发布了 React Native 的 Facebook SDK,使开发人员能够更轻松地将 Facebook 社交功能(如登录、分享、应用分析和 Graph API)集成到他们的应用中。一年来,React Native 改变了开发人员在每个主要平台上构建应用的方式。

这是一段令人兴奋的旅程——但我们才刚刚开始。以下回顾了 React Native 在开源一年来是如何发展和演变的,以及我们在此过程中遇到的一些挑战,以及我们对未来的展望。

这是一个摘录。请在 Facebook Code 上阅读文章的其余部分。

深入 React Native 性能

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

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

为什么着急?

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

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

这是一个摘录。请在 Facebook Code 上阅读文章的其余部分。

介绍热重载

·阅读时间:9 分钟
Martín Bigio
Instagram 软件工程师

React Native 的目标是为您提供尽可能最佳的开发体验。其中很大一部分是您保存文件到能够看到更改之间的时间。我们的目标是使这个反馈循环在 1 秒内完成,即使您的应用不断增长。

我们通过三个主要功能实现了这一目标

  • 使用 JavaScript 作为语言,因为它没有较长的编译周期。
  • 实现了一个名为 Packager 的工具,它将 es6/flow/jsx 文件转换为 VM 可以理解的普通 JavaScript。它被设计为一个服务器,将中间状态保存在内存中以实现快速的增量更改,并使用多个内核。
  • 构建了一个名为 Live Reload 的功能,该功能在保存时重新加载应用。

此时,开发人员的瓶颈不再是重新加载应用所需的时间,而是丢失应用的状态。一个常见的场景是处理一个距离启动屏幕多个屏幕的功能。每次重新加载时,您都必须一次又一次地点击相同的路径才能返回到您的功能,使周期长达数秒。

热重载

热重载背后的理念是在运行时保持应用运行并注入已编辑文件的最新版本。这样,您就不会丢失任何状态,这在您调整 UI 时尤其有用。

一张图片胜过千言万语。查看 Live Reload(当前)和 Hot Reload(新)之间的区别。

如果您仔细观察,您会注意到可以从红色框中恢复,并且还可以开始导入之前不存在的模块,而无需进行完全重新加载。

**警告:**由于 JavaScript 是一种非常有状态的语言,因此无法完美地实现热重载。在实践中,我们发现当前的设置适用于大量常见用例,并且如果出现任何问题,始终可以使用完全重新加载。

热重载从 0.22 版本开始可用,您可以启用它

  • 打开开发者菜单
  • 点击“启用热重载”

简而言之的实现

既然我们已经了解了为什么要使用它以及如何使用它,那么有趣的部分开始了:它实际上是如何工作的。

热重载构建在 热模块替换(或 HMR)功能之上。它最初由 webpack 引入,我们将其在 React Native Packager 中实现。HMR 使 Packager 能够监视文件更改并将 HMR 更新发送到应用中包含的精简 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 的工厂函数都未执行,因此尚未缓存任何导出。然后,用户修改 timeMM/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 被修改时,HMR 进程不能在替换 time 的代码后简单地结束。如果它这样做了,当 log 被执行时,它将使用 time 的缓存副本(旧代码)进行执行。

为了让 log 获取 time 的更改,我们需要清除其缓存的导出,因为其依赖的模块之一已热交换。(步骤 3)。最后,当再次需要 log 时,它的工厂函数将被执行,需要 time 并获取其新代码。

HMR API

React Native 中的 HMR 通过引入 hot 对象扩展了模块系统。此 API 基于 webpack 的 API。hot 对象公开了一个名为 accept 的函数,该函数允许您定义一个回调,当需要热交换模块时将执行该回调。例如,如果我们将 time 的代码更改如下,则每次保存 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,它可以放弃,因为它还没有被 require。对于 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 组件创建一个代理。代理保存组件的状态并将生命周期方法委托给实际的组件,这些组件是我们热重载的对象。

除了创建代理组件之外,转换还定义了 accept 函数,其中包含一段代码来强制 React 重新渲染组件。这样,我们就可以热重载渲染代码,而不会丢失任何应用的状态。

React Native 附带的默认 转换器 使用 babel-preset-react-native,它被配置为以与在使用 Webpack 的 React Web 项目中相同的方式使用 react-transform

Redux 存储

要启用 Redux 存储上的热重载,您只需要类似于在使用 Webpack 的 Web 项目中那样使用 HMR API 即可。

// 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 不知道如何接受自身,因此它会查找所有引用它的模块并尝试接受它们。最终,流程将到达唯一的存储,即 configureStore 模块,该模块将接受 HMR 更新。

结论

如果您有兴趣帮助改进热重载,我鼓励您阅读 Dan Abramov 关于热重载未来的文章 并做出贡献。例如,Johny Days 将要使其与多个连接的客户端一起工作。我们依靠大家来维护和改进此功能。

使用 React Native,我们有机会重新思考构建应用的方式,以使其成为很棒的开发体验。热重载只是拼图的一块,我们还能做哪些疯狂的技巧来使其变得更好?

使 React Native 应用可访问

·阅读时间:2分钟
Georgiy Kassabli
Facebook 软件工程师

随着 React 在 Web 端和 React Native 在移动端的最新发布,我们为开发者提供了一个新的前端框架来构建产品。构建健壮产品的关键方面之一是确保任何人都可以使用它,包括视力障碍或其他残疾人士。React 和 React Native 的辅助功能 API 使您能够使任何由 React 驱动的体验对可能使用辅助技术的人可用,例如盲人和视力障碍人士使用的屏幕阅读器。

对于本文,我们将重点关注 React Native 应用。我们设计了 React 辅助功能 API,使其外观和感觉类似于 Android 和 iOS API。如果您之前开发过 Android、iOS 或 Web 的辅助功能应用,那么您应该对 React AX API 的框架和命名法感到熟悉。例如,您可以使 UI 元素可访问(因此可供辅助技术使用)并使用accessibilityLabel 为该元素提供字符串描述。

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

让我们通过查看 Facebook 自身的一个由 React 驱动的产品:广告管理器应用,来了解 React AX API 的一个稍微复杂一些的应用。

这是摘录。请在 Facebook Code 上阅读文章的其余部分。

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

·阅读时间:1 分钟
Facebook 软件工程师

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

在 Facebook,我们已经使用 React Native 进行了超过一年的生产环境应用。大约一年前,我们的团队着手开发 广告管理器应用。我们的目标是创建一个新的应用,让数百万在 Facebook 上投放广告的人能够随时随地管理他们的账户并创建新的广告。它最终不仅成为 Facebook 的第一个完全使用 React Native 构建的应用,而且还是第一个跨平台应用。在这篇文章中,我们想与大家分享我们是如何构建这个应用的,React Native 如何帮助我们加快开发速度,以及我们学到的经验教训。

这是摘录。请在 Facebook Code 上阅读文章的其余部分。