跳到主要内容

介绍 Button,使用 Yarn 更快的安装,以及公共路线图

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

我们从很多人那里听说,React Native 正在进行大量工作,要跟踪正在发生的事情可能很困难。为了帮助沟通正在进行的工作,我们现在发布了 React Native 的路线图。在较高的层面上,这项工作可以分为三个优先事项

  • 核心库。为最有用的组件和 API 添加更多功能。
  • 稳定性。改进底层基础设施,以减少错误并提高代码质量。
  • 开发者体验。帮助 React Native 开发者更快地行动

如果您对您认为在路线图上很有价值的功能有建议,请查看 Canny,您可以在那里建议新功能并讨论现有提案。

React Native 的新功能

今天发布的 React Native 0.37 版本 引入了一个新的核心组件,可以非常轻松地向任何应用添加可触摸的 Button。我们还引入了对新的 Yarn 包管理器的支持,这应该会加快更新应用依赖项的整个过程。

介绍 Button

今天,我们推出一个基本的 <Button /> 组件,它在每个平台上看起来都很棒。这解决了我们收到的最常见的反馈之一:React Native 是唯一一个没有开箱即用的按钮的移动开发工具包之一。

Simple Button on Android, iOS

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

经验丰富的 React Native 开发者知道如何制作按钮:在 iOS 上使用 TouchableOpacity 以获得默认外观,在 Android 上使用 TouchableNativeFeedback 以获得涟漪效果,然后应用一些样式。自定义按钮的构建或安装并不特别困难,但我们的目标是使 React Native 变得非常容易学习。通过将基本按钮添加到核心组件中,新手将能够在第一天开发出很棒的东西,而不是花费时间格式化按钮和学习 Touchable 的细微差别。

Button 旨在在每个平台上都能出色地工作并看起来原生,因此它不会支持自定义按钮的所有花哨功能。它是一个很好的起点,但并不意味着要替换所有现有按钮。要了解更多信息,请查看新的 Button 文档,其中包含可运行的示例!

使用 Yarn 加速 react-native init

您现在可以使用 Yarn,这个新的 JavaScript 包管理器,来显着加速 react-native init。要查看加速效果,请安装 yarn 并将您的 react-native-cli 升级到 1.2.0

$ npm install -g react-native-cli

您现在应该在设置新应用时看到“Using yarn”

Using yarn

在简单的本地测试中,react-native init良好的网络环境下大约 1 分钟完成(而使用 npm 3.10.8 时约为 3 分钟)。安装 yarn 是可选的,但强烈建议。

谢谢!

我们要感谢为本次发布做出贡献的每个人。完整的发行说明现已在 GitHub 上提供。凭借二十多个错误修复和新功能,React Native 在您的帮助下变得越来越好。

0.36:Headless JS,Keyboard API,以及更多

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

今天我们发布了 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 文档以了解更多信息!

Keyboard API

使用 Keyboard 可以更轻松地使用屏幕键盘。您现在可以监听原生键盘事件并对其做出反应。例如,要关闭活动的键盘,只需调用 Keyboard.dismiss()

import {Keyboard} from 'react-native';

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

Animated Division

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>

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

Dark Status Bars

一个新的 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 核心实现,以及特定的 OSS JS 组件以支持 RTL。

为了在生产环境中对 RTL 支持进行实战测试,最新版本的 Facebook Ads Manager 应用(第一个跨平台 100% RN 应用)现在以阿拉伯语和希伯来语提供,并为 iOSAndroid 提供 RTL 布局。以下是它在这些 RTL 语言中的外观

RN 中 RTL 支持的概述更改

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

此外,css-layout 已经使每个组件的方向从其父组件继承。这意味着,我们只需将根组件的方向设置为 RTL,整个应用就会翻转。

下图描述了高级别的更改

这些包括

通过此更新,当您允许应用的 RTL 布局时

  • 每个组件布局将水平翻转
  • 如果您使用 RTL 就绪的 OSS 组件,则某些手势和动画将自动具有 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 布局中以相同的方式显示,因为 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。

说明手势 RTL 支持的一个很好的例子是 SwipeableRow

手势示例
// 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 Meetup。大约有 200 人参加,这是一个与我附近也对 React Native 感兴趣的其他开发者见面的好地方。

我特别有兴趣了解更多关于 React 和 React Native 如何在 Zynga、Netflix 和 Airbnb 等公司中使用。当晚的议程如下

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

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

如果 这些聚会之一 在您附近举行,我强烈建议参加!

Zynga 的 React 快速原型设计

第一轮新闻之后是 Zynga 的快速介绍,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

每种方法都有其自身的考虑因素,这取决于您来确定哪种方法最适合您的需求。

方法 #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 首先谈到了绿地应用与棕地应用。绿地意味着开始一个项目时无需考虑任何先前的工作。这与棕地项目形成对比,在棕地项目中,您需要考虑现有项目的需求、开发流程以及所有团队的各种需求。

当您在开发绿地应用时,React Native CLI 会为 Android 和 iOS 设置一个单独的仓库,并且一切正常。在 Airbnb 使用 React Native 的第一个挑战是,Android 和 iOS 应用各有自己的仓库。多仓库公司在采用 React Native 之前需要克服一些障碍。

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

这确实带来了一些缺点,主要是他们无法发布原子更新。需要原生和 JavaScript 代码组合的更改将需要三个单独的拉取请求,所有这些请求都必须仔细地合并。为了避免冲突,如果自从构建开始以来 master 已更改,CI 将无法将更改合并回 Android 和 iOS 仓库。这会在高提交频率的日子(例如,当发布新版本时)导致长时间的延迟。

自那时以来,Airbnb 已转向单仓库方法。幸运的是,这已经在考虑之中,一旦 Android 和 iOS 团队适应了使用 React Native,他们很乐意加速转向单仓库。

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

导航问题

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

实际上,现有的导航库似乎都不太适合棕地应用。棕地应用要求导航状态完全由原生应用拥有。例如,如果用户的会话在呈现 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 新闻结束

聚会提供了一个很好的机会,可以与其他社区开发者见面并学习。我期待将来参加更多 React Native 聚会。如果你参加了其中一个聚会,请留意我,并告诉我我们如何才能让 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);

我们认为这些内联示例,使用 react-native-web-player 模块并在 Devin Abbott 的帮助下,是学习 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 中布局的所有属性列表

入门指南

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

我们构建了一个 新的入门指南工作流程,让您可以预先选择您的开发操作系统和移动操作系统,从而在一个简洁的地方提供所有设置说明。我们还经历了安装过程,以确保一切正常工作,并确保每个决策点都有明确的建议。在我们的无辜同事身上测试之后,我们非常确定这是一个改进。

我们还致力于 将 React Native 集成到现有应用中的指南。许多使用 React Native 的最大型应用,例如 Facebook 应用本身,实际上是在 React Native 中构建应用的一部分,另一部分使用常规开发工具构建。我们希望本指南能让更多人更容易以这种方式构建应用。

我们需要您的帮助

您的反馈让我们知道我们应该优先处理什么。我知道有些人会阅读这篇博文,并认为“更好的文档?切!X 的文档仍然是垃圾!”。这很棒——我们需要这种活力。给我们反馈的最佳方式取决于反馈的类型。

如果您在文档中发现错误,例如不准确的描述或实际上无法工作的代码,请提交 issue。用“Documentation”标记它,以便更容易将其路由给合适的人。

如果没有具体的错误,但是文档中的某些内容从根本上令人困惑,则不适合提交 GitHub issue。相反,在 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 允许您使用 React 和 Relay 的声明式编程模型在 JavaScript 中构建 Android 和 iOS 应用。这带来了更简洁、更易于理解的代码;无需编译周期的快速迭代;以及跨多个平台轻松共享代码。您可以更快地交付,并专注于真正重要的细节,使您的应用看起来和感觉都很棒。优化性能是其中重要的一部分。这是我们如何使 React Native 应用启动速度提高一倍的故事。

为什么要赶时间?

对于运行更快的应用,内容加载速度更快,这意味着人们有更多时间与之互动,流畅的动画使应用使用起来令人愉快。在新兴市场,2011 年的低端手机2G 网络中占多数,对性能的关注可能会决定一个应用是可用还是不可用。

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

这是一段摘录。在 Facebook Code 上阅读帖子的其余部分。

介绍 Hot Reloading

·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,它可以放弃,因为它尚未被需要。对于 MovieSearch,它将必须清除其导出并递归地处理其父模块。最后,它将对 MovieRouter 执行相同的操作,并在那里结束,因为没有模块依赖于它。

为了遍历依赖关系树,运行时从 Packager 接收 HMR 更新上的反向依赖关系树。对于此示例,运行时将接收到一个类似这样的 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 transform,它使用 webpack 的 HMR API 来解决这个问题。简而言之,他的解决方案通过在转换时为每个 React 组件创建一个代理来工作。代理保存组件的状态,并将生命周期方法委托给实际组件,而实际组件正是我们热重载的组件

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

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

Redux Store

要在 Redux store 上启用热重载,您只需要使用 HMR API,类似于您在使用 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,我们有机会重新思考构建应用的方式,以使其成为出色的开发者体验。热重载只是难题的一部分,我们还能做哪些疯狂的 hack 来使其变得更好?

使 React Native 应用更易于访问

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

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

在这篇文章中,我们将重点关注 React Native 应用。我们设计的 React Accessibility 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 上阅读帖子的其余部分。