跳到主要内容

26 篇博文 含有标签「engineering」

查看所有标签

介绍 Create React Native App

· 阅读需 2 分钟
Adam Perry
Expo 软件工程师

今天我们发布了 Create React Native App:一款让启动 React Native 项目变得更加简单的新工具!它深受 Create React App 设计的启发,是 Facebook 和 Expo(原名 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-maps 或 Facebook 认证。不过如果你需要 Expo 未捆绑的原生代码依赖,可能需要自行配置构建。和 Create React App 一样,CRNA 支持“弹出”(eject)。

你可以运行 npm run eject 来获得一个非常类似于 react-native init 生成的项目。此时你需要使用 Xcode 和/或 Android Studio,使用 react-native link 添加库,且能完全控制原生代码的编译过程。

有疑问?反馈?​

Create React Native App 现在已足够稳定可供通用,欢迎大家分享使用体验!你可以在 Twitter 上找到我,或者在 GitHub 仓库 提交问题。欢迎贡献代码!

使用原生驱动实现动画

· 阅读需 6 分钟
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 的支持,并修复了我们在生产应用中遇到的各种 bug。这真的是一次社区协作,我要感谢所有参与者,也感谢 Expo 对开发的大力支持。如今它被 React Native 的 Touchable 组件和新发布的React Navigation库中的导航动画广泛使用。

它是如何工作的?​

首先,让我们看看使用 JS 驱动的 Animated 动画是如何工作的。当使用 Animated 时,你声明一个动画节点图,表示你想执行的动画,然后用驱动器根据预设曲线实时更新 Animated 值。你也可以通过 Animated.event 将 Animated 值连接到 View 的事件。

动画各步骤及其所在线程说明:

  • JS:动画驱动器使用 requestAnimationFrame 在每帧执行,基于动画曲线计算新值并更新目标值。
  • JS:计算中间值并传递到附加到 View 的 props 节点。
  • JS:通过 setNativeProps 更新 View。
  • JS 到 Native 桥接。
  • Native:更新 UIView 或 android.View。

如你所见,大部分工作都在 JS 线程上。如果 JS 线程被阻塞,动画会跳帧。同时,动画每帧都需要通过 JS 到 Native 桥接更新原生视图。

而原生驱动就是把这些步骤全部转到原生执行。由于 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 节点,告诉驱动附加的是视图的哪个属性:

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 计算值。

剩下的就是通过指定动画曲线类型和要更新的 Animated 值来启动动画。时间动画也可以通过在 JS 预先计算所有帧,简化原生实现。

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

动画运行时的流程:

  • Native:原生动画驱动使用 CADisplayLink 或 android.view.Choreographer 在每帧执行,基于动画曲线计算新值更新动画值。
  • Native:计算中间值并传递到附加到原生视图的 props 节点。
  • Native:更新 UIView 或 android.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, // <-- 添加这一行
}).start();

一个 Animated 值只兼容一种驱动,如果你用原生驱动启动了某个值的动画,确保该值的所有动画都用原生驱动。

它同样支持 Animated.event,这在动画需跟随滚动位置时非常有用。没有原生驱动时,由于 React Native 的异步特性,动画总是滞后于手势一帧。

之前:

<ScrollView
scrollEventThrottle={16}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }]
)}
>
{content}
</ScrollView>

之后:

<Animated.ScrollView // <-- 使用 Animated 的 ScrollView 包装器
scrollEventThrottle={1} // <-- 设置 1 保证不丢失任何事件
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.animatedValue } } }],
{ useNativeDriver: true } // <-- 添加这一行
)}
>
{content}
</Animated.ScrollView>

注意事项​

并不是 Animated 所支持的所有功能,原生驱动都支持。主要限制是你只能动画非布局属性,比如 transform 和 opacity 可用,但 Flexbox 以及 position 属性不可用。另一个限制是 Animated.event 只支持直接事件,不支持冒泡事件,这意味着不能与 PanResponder 一起用,但支持 ScrollView#onScroll 这类事件。

Native Animated 已经存在 React Native 很久了,但一直没有文档,因为它之前被视为实验性质。所以请确保使用的是较新的 React Native 版本(0.40 及以上)才能使用该功能。

参考资源​

想了解更多关于 Animated 的内容,我推荐观看 Christopher Chedeau 的 这场演讲。

如果你想深入了解动画以及把动画卸载给原生如何提升用户体验,可以看 Krzysztof Magiera 的 这场讲座。

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

· 阅读需 8 分钟
王梦珏 (Mandy)
Facebook 软件工程实习生

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

我们很高兴地宣布,React Native 已经改进,支持 RTL 布局。该功能现已在 react-native 主分支可用,并将在下一个发行候选版本中提供: v0.33.0-rc。

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

为了在生产环境中测试 RTL 支持,最新版的 Facebook Ads Manager 应用(首个 100% 跨平台 RN 应用)现已支持阿拉伯语和希伯来语的 RTL 布局,分别适用于 iOS 和 Android。下面是这些 RTL 语言下的界面展示:

RN 支持 RTL 的改动概述​

css-layout 已经有了 start 和 end 的布局概念。在从左到右(LTR)布局中,start 表示左边,end 表示右边。但在 RTL 中,start 表示右边,end 表示左边。这意味着我们可以依赖 start 和 end 的计算来确定正确的布局,这包括 position、padding 和 margin。

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

下面的图表描述了高层次的改动:

改动包括:

有了这个更新,当你允许你的应用使用 RTL 布局时:

  • 每个组件的布局会水平翻转
  • 如果你使用支持 RTL 的 OSS 组件,部分手势和动画会自动适配 RTL 布局
  • 你只需做最小的额外工作,就能使应用完全支持 RTL

让应用支持 RTL​

  1. 要支持 RTL,首先应将 RTL 语言包加入你的应用。

  2. 通过在原生代码开头调用 allowRTL() 函数,允许你的应用使用 RTL 布局。这个工具仅会启用 RTL 布局,当你的应用已准备好。示例如下:

    iOS:

    // 在 AppDelegate.m 中
    [[RCTI18nUtil sharedInstance] allowRTL:YES];

    Android:

    // 在 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 中,手势和动画不在核心代码层支持,而是在组件层。好消息是,部分组件今天已支持 RTL,如 SwipeableRow 和 NavigationExperimental。但其他带有手势的组件还需手动支持 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={'快速测试 RTL 布局'}>
<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(
'重新加载页面',
'请重新加载此页面以更改界面方向!' +
'本应用内所有示例都会受到影响,' +
'可以查看它们在 RTL 布局下的表现。',
);
};

开发新功能时,你可以轻松切换这个按钮并重启应用来查看 RTL 布局。好处是,无需更改语言设置来测试,但部分文本对齐不会随之变化(参见下一节说明)。因此,发布前始终推荐在 RTL 语言环境下完整测试应用。

限制与未来计划​

RTL 支持应该涵盖大部分应用用户体验,但目前仍有一些限制:

  • Android 与 iOS 中文本对齐行为不同
    • iOS 中,默认文本对齐依赖于所用语言包,始终保持在同一侧。Android 中,默认文本对齐依赖文本内容语言,即英文左对齐,阿拉伯文右对齐。
    • 理论上,应该统一平台间的行为,但不同用户可能对不同行为有偏好。未来或需更多用户体验调研以确定最佳实践。
  • 没有“真实”的左/右

    如前所述,我们将 JS 端的 left/right 样式映射为 start/end,代码中写的 left 在 RTL 时显示为屏幕右侧,right 则显示为屏幕左侧。这方便产品代码改动最小,但目前没办法指定“真实的左边”或“真实的右边”。未来可能需要允许组件无视语言设置,自行控制方向。

  • 让手势与动画的 RTL 支持更易用

    目前,让手势和动画支持 RTL 仍需一定编码工作。未来有望找到更简便的方式帮助开发者支持 RTL 的手势和动画。

尝试一下吧!​

查看 RNTester 中的 RTLExample,深入理解 RTL 支持,并告诉我们你的使用体验!

最后,感谢阅读!希望 React Native 的 RTL 支持能助你让应用走向国际,更好地成长!

深入探讨 React Native 性能

· 阅读需 2 分钟
Pieter De Baets
Facebook 软件工程师

React Native 允许你使用 JavaScript 结合 React 和 Relay 的声明式编程模型构建 Android 和 iOS 应用。这带来了代码更简洁、更易理解;无需编译周期即可快速迭代;以及跨多个平台轻松共享代码。你可以更快发布,专注于真正重要的细节,让你的应用看起来和使用起来都非常出色。优化性能是其中的重要环节。这里讲述了我们如何让 React Native 应用启动速度提升一倍的故事。

为什么要加快速度?​

应用运行更快,内容加载更快,意味着用户有更多时间与应用互动,流畅的动画让应用使用体验更加愉悦。在新兴市场,比如绝大多数用户使用的是2011年款手机并连结于2G网络,性能优化往往决定了应用是能用还是几乎无法使用。

自从 React Native 在iOS 和Android 平台发布以来,我们一直在提升列表视图滚动性能、内存效率、界面响应速度和应用启动时间。启动时间决定了应用的第一印象,并且考验框架的各个部分,因此是最有价值且最具挑战性的问题。

以上为节选内容。请在Facebook Code阅读完整文章。

介绍热重载

· 阅读需 8 分钟
Martín Bigio
Instagram 软件工程师

React Native 的目标是为你提供最佳的开发者体验。重要的一部分是从你保存文件到能够看到更改之间所花费的时间。我们的目标是把这个反馈循环控制在 1 秒以内,即使你的应用日益庞大。

我们通过三个主要特性接近了这个理想:

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

此时,开发者的瓶颈不再是应用加载时间,而是丢失应用状态。一个常见场景是你正在开发一个多屏幕外的功能。每次重载,你都必须反复点击同一路径返回功能界面,使得循环花费数秒。

热重载​

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

一张图胜千言。看看 Live Reload(当前)和 Hot Reload(新)的区别。

仔细看,你会发现它可以从红色错误界面恢复,也可以开始导入之前不存在的模块,无需完整重载。

警告: 由于 JavaScript 是一个高度状态化的语言,热重载无法完美实现。实际上,我们发现当前的方案已能较好地应对大多数常用场景,且如若发生错误总可以进行完整重载。

热重载自 0.22 版本起可用,你可以这样开启:

  • 打开开发者菜单
  • 点击“Enable Hot Reloading”

实现简述​

了解了为什么要用以及如何使用后,趣味部分来了:它实际上是如何工作的。

热重载基于一种名为 Hot Module Replacement(简称 HMR)的功能。这最初由 webpack 引入,我们在 React Native Packager 中实现了它。HMR 使 Packager 监听文件变更,并向应用中包含的轻量 HMR 运行时发送更新。

简言之,HMR 更新包含了修改后 JS 模块的新代码。运行时收到后,会用新代码替换旧代码:

HMR 更新不仅仅包含要更改模块的代码,因为光替换代码还不足以让运行时认知变更。问题在于模块系统可能已缓存了想要更新模块的 exports。举例说明,假设应用由以下两个模块组成:

// 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 函数注册每个模块到模块系统。对于该应用,log 模块会有如下定义:

__d('log', function() {
... // 模块代码
});

该调用将每个模块代码包裹在匿名函数中,通常称之为工厂函数。模块系统运行时跟踪每个模块的工厂函数,是否已执行过,以及执行结果(exports)。当某模块被引用,模块系统会返回缓存的 exports,或者首次执行工厂函数并缓存结果。

假设启动应用并引用 log 模块,此时 log 和 time 的工厂函数都未执行,exports 还未缓存。之后用户修改 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 模块中的 require('time') 是顶级调用:

const time = require('./time'); // 顶级 require

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

module.exports = log;

当 log 被引用时,运行时会缓存它和 time 的 exports(步骤 1)。之后 time 变更时,HMR 进程不能只替换 time 的代码直接结束,否则 log 执行时仍会使用缓存的旧 time 代码。

为了让 log 拥抱 time 的改变,必须清除 log 的缓存 exports,因为它依赖的模块被热替换了(步骤 3)。最后,下一次 log 被引用时,工厂函数会被重新执行,进而使用新 time 代码。

HMR API​

React Native 中的 HMR 扩展了模块系统,新增了 hot 对象。该 API 基于 webpack 的实现。hot 对象暴露了 accept 函数,允许你定义模块被热替换时调用的回调。例如,如果我们修改 time,每次保存时,控制台都会输出 “time changed”:

// time.js
function time() {
... // 新代码
}

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

module.exports = time;

注意手动使用该 API 的情况很少见。热重载默认对大多数场景开箱即用。

HMR 运行时​

如前所述,仅仅接受 HMR 更新有时不够,因为使用该模块的上层模块可能已执行且导入被缓存。举例来说,假设电影示例应用的依赖树顶层有个 MovieRouter,其依赖 MovieSearch 和 MovieScreen 视图,这些视图又依赖前面示例中的 log 和 time 模块:

用户访问了电影搜索视图,但未访问另一个视图,除 MovieScreen 外,其他模块都缓存了 exports。如果修改了 time,运行时会清除 log 的缓存 exports 以反映 time 的变更。这个过程还会递归上溯所有父模块并尝试接受它们的更新。对于未被引用的 MovieScreen,会跳过;对 MovieSearch,清除缓存后递归处理其父级;最终对 MovieRouter 采用相同处理,之后结束,因为没有父模块依赖它。

运行时为进行依赖树递归,会从 Packager 那里在 HMR 更新中获得逆向依赖树。对于此例,运行时会收到了如下类型的 JSON 对象:

{
modules: [
{
name: 'time',
code: /* time 的新代码 */
}
],

inverseDependencies: {
MovieRouter: [],
MovieScreen: ['MovieRouter'],
MovieSearch: ['MovieRouter'],
log: ['MovieScreen', 'MovieSearch'],
time: ['log'],
}
}

React 组件​

React 组件更难支持热重载,因为不能简单替换旧代码否则会丢失组件状态。对于 React Web 应用,Dan Abramov 实现了 babel transform,结合 webpack 的 HMR API 解决了这个问题。简而言之,他的方案是在 transform 阶段 为每个 React 组件创建一个代理。这些代理持有组件状态,并将生命周期方法委托给实际组件,后者是被热重载的目标:

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

React Native 默认的 transformer 使用 babel-preset-react-native,其中 配置 了 react-transform,用法与 React Web + webpack 项目相同。

Redux Store​

要为 Redux store 启用热重载,只需像在 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 自身无法接受热替换,因此会查找引用它的所有模块并尝试接受。最终,这个流程会到达单一的 store —— configureStore 模块,在那里接受 HMR 更新。

总结​

如果你有兴趣帮助改进热重载,建议阅读 Dan Abramov 关于热重载未来的文章 并参与贡献。例如,Johny Days 正在 实现支持多客户端连接。我们期待大家一起维护和改进这项功能。

React Native 给了我们机会重新思考构建应用的方式,以打造极佳的开发体验。热重载只是拼图中的一片,还有哪些疯狂的黑科技,我们能让开发体验更好呢?

让 React Native 应用支持无障碍功能

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

随着 React 在网页端和 React Native 在移动端的推出,我们为开发者提供了一个全新的前端框架以构建产品。构建一个强健产品的关键之一是确保任何人都能使用它,包括视力障碍或其他残疾人士。React 和 React Native 的无障碍 API 使您能够让任何基于 React 的体验为可能使用辅助技术的人所用,比如盲人和视障者使用的屏幕阅读器。

在本文中,我们将重点介绍 React Native 应用。我们设计的 React 无障碍 API 与 Android 和 iOS 的 API 看起来和感觉上都相似。如果你之前为 Android、iOS 或网页开发过无障碍应用,你应该会对 React AX API 的框架和命名感到熟悉。例如,你可以让一个 UI 元素 accessible(因此它能被辅助技术访问),并使用 accessibilityLabel 来为该元素提供一个字符串描述:

<View accessible={true} accessibilityLabel=”这是一个简单视图”>

让我们通过看看 Facebook 自家的一个基于 React 的产品 —— 广告管理器应用 来演示 React AX API 更加复杂的应用场景。

这是节选内容。阅读全文请访问 Facebook Code。