跳到主要内容

为 Animated 使用 Native Driver

·7 分钟阅读
Janic Duplessis
App & Flow 的软件工程师

在过去的一年里,我们一直在努力改进使用 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 的 props 节点。
  • JS:使用 setNativeProps 更新 View
  • JS 到原生桥接器。
  • 原生: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',
});

创建原生 props 节点,这告诉原生驱动程序它所附加的视图上的哪个 prop

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

将节点连接在一起

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

将 props 节点连接到视图

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

这样,原生动画模块就拥有了更新原生视图所需的所有信息,而无需转到 JS 来计算任何值。

剩下的就是通过指定我们想要的动画曲线类型以及要更新的动画值来实际启动动画。定时动画也可以通过预先在 JS 中计算动画的每一帧来简化,从而使原生实现更小。

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

现在这是动画运行时发生情况的细分

  • 原生:原生动画驱动程序使用 CADisplayLinkandroid.view.Choreographer 在每一帧上执行,并使用基于动画曲线计算的新值更新它驱动的值。
  • 原生:计算中间值并将其传递给附加到原生视图的 props 节点。
  • 原生: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 的所有功能目前都在 Native Animated 中受支持。主要的限制是您只能为非布局属性设置动画,例如 transformopacity 可以工作,但 Flexbox 和 position 属性则不行。另一个限制是 Animated.event,它仅适用于直接事件,而不适用于冒泡事件。这意味着它不适用于 PanResponder,但适用于 ScrollView#onScroll 之类的事件。

Native Animated 也已经成为 React Native 的一部分很长时间了,但从未被记录下来,因为它被认为是实验性的。因此,如果您想使用此功能,请确保您使用的是 React Native 的最新版本 (0.40+)。

资源

有关动画的更多信息,我建议观看 Christopher Chedeau演讲

如果您想深入了解动画以及将动画卸载到原生端如何改善用户体验,还可以观看 Krzysztof Magiera演讲

每月发布节奏:发布十二月和一月 RC

·2 分钟阅读
Eric Vicenti
Facebook 工程师

在 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,升级更轻松

·4 分钟阅读
Nicolas Cuillery
Zenika 的 JavaScript 顾问和培训师

升级到新版本的 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 仓库。它不会干扰您的项目仓库(无论您使用什么 VCS: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 能够使用其 3 向合并算法自动合并大多数更改,并最终给我们留下熟悉的冲突分隔符

    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 版本开始。如果我们将实现嵌入到 local-cli 中,那么当您使用旧版本的 React Native 时,您将无法享受此功能。例如,如果此新的升级代码仅在 0.38.0 中发布,您将无法从 0.29.2 升级到 0.38.0。

基于 Git 的升级是开发者体验的巨大改进,并且让每个人都能使用它非常重要。通过使用单独的全局安装的包 react-native-git-upgrade,无论您的项目使用哪个版本的 React Native,您今天都可以使用这个新代码。

另一个原因是 Martin Konicek 最近的 Yeoman wipeout。我们不想将这些 Yeoman 依赖项重新放回 react-native 包中,以便能够评估旧模板以创建补丁。

试用并提供反馈

总而言之,我想说,享受这个功能,并随时提出改进建议、报告问题,尤其是发送拉取请求。每个环境都略有不同,每个 React Native 项目也各不相同,我们需要您的反馈才能使它为每个人都很好地工作。

谢谢!

我要感谢很棒的公司 ZenikaM6 Web (已存档),没有他们,这一切都不可能实现!

介绍 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 极易学习。通过在核心中添加基本按钮,新手将能够在第一天开发出很棒的东西,而不是花费时间格式化 Button 并了解 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

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

Using yarn

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

谢谢!

我们要感谢为本次发布做出贡献的所有人。完整的发行说明现在已在 GitHub 上提供。凭借超过 20 多个错误修复和新功能,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>

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

深色状态栏

一个新的 barStyle 值已添加到 StatusBardark-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

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 广告管理工具应用(第一个跨平台 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 的 OSS 组件
  • 可能需要少量额外的工作来使您的应用完全支持 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 和 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

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

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

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

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

方法 #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 仓库镜像到这个新仓库中。在运行测试并构建 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 被提及为似乎很有前景的东西,但最终并不适合他们的用例。

实际上,现有的导航库似乎都不适用于棕地应用。棕地应用要求导航状态完全由原生应用拥有。例如,如果用户会话在呈现 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。如果您参加了其中一个,请留意我,并告诉我我们如何才能使 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 中所有控制布局的 prop 列表

入门

当您开始在您的机器上设置 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 大会上,我们宣布 Microsoft 正在将 React Native 带入 Windows 生态系统,使开发者有可能在 Windows PC、手机和 Xbox 上构建 React Native。它还将提供开源工具和服务,例如用于 Visual Studio Code 的 React Native 扩展和 CodePush,以帮助开发者在 Windows 平台上创建 React Native 应用。此外,Samsung 正在为其混合平台构建 React Native,这将使开发者能够为数百万台智能电视以及移动和可穿戴设备构建应用。我们还发布了 React Native 的 Facebook SDK,这使开发者可以更轻松地将 Facebook 社交功能(如登录、分享、应用分析和 Graph API)整合到他们的应用中。在一年内,React Native 改变了开发者在每个主要平台上构建应用的方式。

这是一段史诗般的旅程 — 但我们才刚刚开始。回顾一下 React Native 自从一年前开源以来是如何成长和演变的,我们在这一过程中面临的一些挑战,以及我们对未来发展的期望。

这是一段摘录。在 Facebook 代码博客 上阅读帖子的其余部分。