跳到主要内容

迈向更好的文档

·阅读 5 分钟
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 上显示 TextView 等 React Native 组件的方式。如果您有兴趣构建共享大部分代码库的移动和 Web 体验,请查看 react-native-web

更好的指南

在 React Native 的某些部分,有多种实现方式,我们收到了反馈,表示我们可以提供更好的指导。

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

我们还有一份新的处理触摸指南,它解释了制作类似按钮界面的基础知识,并简要概述了处理触摸事件的不同方式。

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

开始入门

当您开始在您的机器上设置 React Native 开发环境时,您确实需要进行大量的安装和配置工作。让安装过程变得真正有趣和令人兴奋是困难的,但我们至少可以使其尽可能快速和无痛。

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

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

我们需要您的帮助

您的反馈让我们知道应该优先处理什么。我知道有些人会读完这篇博客文章,然后想“更好的文档?哼。X 的文档仍然是一堆垃圾!”。这很好——我们需要这种能量。给我们反馈的最佳方式取决于反馈的类型。

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

如果没有具体的错误,但文档中的某些内容从根本上令人困惑,那么这不太适合作为 GitHub 问题。相反,请在 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 应用。此外,三星正在为其混合平台构建 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 上阅读全文。

介绍热重载

·阅读 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 版本开始可用,您可以启用它

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

实现简述

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

热重载是基于 热模块替换(Hot Module Replacement,简称 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 的工厂函数都未执行,因此没有导出被缓存。然后,用户修改 time 以返回 MM/DD 格式的日期。

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

module.exports = bar;

打包器会将 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 执行相同的操作并在那里结束,因为没有模块依赖于它。

为了遍历依赖树,运行时会在 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,该预设配置为使用 react-transform,方式与您在使用 webpack 的 React Web 项目中使用它的方式相同。

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 在 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 应用

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

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

在 Facebook,我们使用 React Native 进行生产已超过一年。大约一年前,我们的团队着手开发广告管理应用。我们的目标是创建一个新的应用程序,让数百万在 Facebook 上投放广告的人能够随时随地管理他们的账户和创建新广告。它最终不仅成为 Facebook 第一个完全基于 React Native 的应用,也是第一个跨平台应用。在这篇文章中,我们想与您分享我们如何构建这个应用程序,React Native 如何使我们能够更快地行动,以及我们学到的经验教训。

这是一篇节选文章。请在 Facebook Code 上阅读全文。

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

·阅读 3 分钟
Tom Occhino
Facebook 工程经理

两年前,我们将 React 引入世界,从那时起,它在 Facebook 内部和外部都取得了令人瞩目的增长。今天,尽管没有人被强制使用它,但 Facebook 的新 Web 项目通常以某种形式使用 React 构建,并且它在整个行业中得到了广泛采用。工程师们每天都选择使用 React,因为它使他们能够花费更多时间专注于产品,而不是与框架作斗争。然而,直到我们使用 React 构建了一段时间后,我们才开始理解它为何如此强大。

React 强制我们将应用程序分解成离散的组件,每个组件代表一个单一视图。这些组件使我们更容易迭代产品,因为我们无需在脑中记住整个系统来更改其中一部分。更重要的是,React 用声明式 API 包装了 DOM 的可变、命令式 API,这提高了抽象级别并简化了编程模型。我们发现,当使用 React 构建时,我们的代码更具可预测性。这种可预测性使我们能够更快速、自信地迭代,并且我们的应用程序也因此更加可靠。此外,不仅用 React 构建的应用程序更容易扩展,我们还发现团队本身的规模也更容易扩展。

结合 Web 的快速迭代周期,我们已经能够用 React 构建出一些很棒的产品,包括 Facebook.com 的许多组件。此外,我们还在 React 之上用 JavaScript 构建了令人惊叹的框架,例如 Relay,它使我们能够大规模地极大简化数据获取。当然,Web 只是故事的一部分。Facebook 还有广泛使用的 Android 和 iOS 应用程序,它们是基于分离的、专有的技术栈构建的。不得不在多个平台之上构建应用程序使我们的工程组织分化,但这只是使原生移动应用程序开发变得困难的原因之一。

这是一篇节选文章。请在 Facebook Code 上阅读全文。