使用React Native来撰写跨平台的App

时间:2023-03-08 16:44:02

React Native 是一个 JavaScript 的框架,用来撰写实时的、可原生呈现 iOS 和 Android 的应用。其是基于 React的,而 React 是 Facebook 的用于构建用户界面的 JavaScript 库,但是这里不是给浏览器解释的,而是为移动平台。换句话说:如果你是一名 web 开发者,你可以使用熟悉的框架和单一的 JavaScript 代码库,即 React Native来撰写清晰的、高效的移动应用。

我们以前都听说过那些个通用的 app 开发,比如框架 Cordova 和 Titanium,那实际使用 React Native 是一种什么样的情况了呢?在本文中,我们会解释 React Native 到底是什么,以及其是如何工作的,再然后我们以撰写实际的 iOS 和 Android 应用来一探 React Native 究竟。在最后,希望读者能够看到有足够的理由在下一个移动应用中选择使用 React Native。

那么什么是 React Native?

在我们深入到开发的经历之前,我们先来探讨下 React Native是什么,并花一些时间来讲述下它是如何工作的。

也只是 React

React 是用于构建用户界面,通常是基于 web 的 JavaScript 程序库。由 Facebook 开发并在2013年将其开源,React 已经得到了颇为广泛的使用。但是其使用的范围比较狭窄,它仅是用于渲染用户的应用程序的界面,而不是更大的 MVC 框架。

React 让开发者们纷涌而至的原因有很多,诸如轻量级、骄人的性能、尤其是可以快速的变更数据。这均与它的组件结构密切相关,它也同样鼓励人们去撰写更加模块化的、可重用的代码。

React Native 也只是 React,但是是针对移动设备的。也有一些少许的不一样的地方,比如开发者需要使用<View>组件而不是<div><Image>代替<img>。开发者的体验多数是一致的。当然,有一些 Objective-C 和 Java 知识是非常有用的,移动开发有其自身的一些颇为棘手的问题(我在多个移动设备上测试过没有?触摸屏的目标是否够大?)。即使是这样,若是开发者曾经有过在浏览器下大 React 经验的话,开发 React Native 会感觉非常的熟悉,并且不会感到不适应。

它是真正的原生

关于 React Native 最为让人们称奇的第一件事就是它是”真正的“原生。其它的用于移动设备的 JavaScript 均是以某种包装后的 Web 视角封装了用户的 JavaScripts 代码。他们也许也重新实现了一些本地 UI 的行为,诸如动画之类的,但是用户写的仍然是基于 Web 的应用。

在React中,一个组件能够描述自身的外观;React 处理后再呈现给用户。一个非常清晰的抽象层将两个功能给隔离开来。为了呈现组件给 web,React 使用的是标准的 HTML 标签。这和抽象层是一个道理,即是“桥”的概念,去让 React Native 调用实际的 iOS 和 Android 呈现的 API。在 iOS 中,这意味着 React Native 组件呈现给真正的 UI 视图;在 Android 下,他们也呈现给了本地的视图。

使用React Native来撰写跨平台的App

你将写出看起来差不多就是标准的 JavaScript、CSS、HTML 代码。取代编译为本地代码,React Native 会将你的应用运行在本地平台的 JavaScript 引擎中,毋需屏蔽主要的 UI 线程。你获得了本地的性能、动画、行为,还不用去写 Object-C 或 Java。其它诸如 Cordova 或 Titanium 之类的跨平台应用开发,是无法达到如此级别的本地性能和表现的。

更好的开发者体验

相比较于 iOS 和 Android 原生的开发,React Native 提供更好的开发者体验。因为你的程序大多数都是 JavaScript,你可以从 web 开发中汲取大量的经验,比如能够立即“刷新”你的应用来查看你代码的修改。相比于在传统的应用开发中花很长的时间去等待构建的过程,会让人感觉这简直是天赐之物。

另外,React Native 还为开发者提供了智能的错误报告和标准的 JavaSript 调试工具,这些让移动开发更加的顺手。

使用React Native来撰写跨平台的App

掌控多平台

React Native 可以平滑的掌控多个平台。绝大多数的 React Native API 都是跨平台的,所以开发者只需要去写 React Native 的组件即可,它可以无缝的运行在 iOS 和 Android 下,脸书声称他们的广告管理应用跨两个平台有87% 的代码可重用,还有,我自己所写的 flashcard 的代码没有任何平台有关的代码。

如果开发者打算撰写特定平台的代码,对于 iOS 和 Android 有不同的交互向导,或者干脆点说,开发者想要获得特定平台 API 的独特优势,也是很容易就可以办到的。React Native 允许开发者为每个组件指定特定平台的版本,可以随意集成到其它 React Native 的应用当中。

使用 React Native

使用单一的 JavaScript 库就想写出真正的原生的 iOS 和 Android 的应用来,无异于异想天开。那么 React Native 又是如何做到的了呢?

上手入门

要开始开发 React Native 应用程序,需要为 iOS 和 Android 的开发安装一些常见的依赖,即使是 React Native。在 React Native 官方站点上有非常好的指南。设置 React Native 其实蛮简单,如果开发者已经安装了最新版的 Node.js 的话,只需要执行命令 npm install -g react-native-cli 就可以安装 React Native了。

一旦这些相关的依赖安装完成后,运行 react-native init ProjectName ,此命令会自动生成开发者开始一个项目所需要的所有样板。

这里有一点需要特别注意:开发 React Native 的话需要使用 OS X 操作系统。开发 iOS 应用,苹果公司强制要求使用 Mac,这一点对于大多数开发人员来说这个限制已经不可避免。如果开发者是专门撰写 Android 应用的话,React Native 还支持 Windows 和 Linux 平台,不过目前尚处于试验阶段,开发者不妨尝试一下。

常见 React 组件

一旦准备好了开发环境,那么就可以开始撰写一些真正的应用程序了。

正如在文章前面所提到的那样,React Native 真的也只是 React,只是有少许不同罢了。React Native 的组件在浏览器中看的话和 React 的组件及其相似,但是基本的构建块已经变了。诸如<div><img><p>等标签均被替代了,React Native 提供给开发者一些诸如<Text><View>等基本的组件,在下面的例子中,基本的组件使用的是<ScrollView><TouchableHighlight、以及<Text>,所有这些都会映射到 iOS 和 Android 特定的视图,使用它们创建带有触摸控制属性的滚动视图是非常直接的:

// iOS & Android

var React = require('react-native');
var { ScrollView, TouchableHighlight, Text } = React; var TouchDemo = React.createClass({
render: function() {
return (
<ScrollView>
<TouchableHighlight onPress={() => console.log('pressed')}>
<Text>Proper Touch Handling</Text>
</TouchableHighlight>
</ScrollView>
);
},
});

如果你没有处理过杂乱无章的 HTML-esque 语法以及 JSX 以前的 JavaScript 的话,这可能对你有一些困惑。React 强烈推荐开发者使用 JSX,那么基于 React Native 的话,其实没有什么可以选择的机会。你渲染的标记是和 JavaScript 所控制的行为共存的。就这一点来说常会引起新接触者强烈的反感,但是我还是强烈建议不妨一试。

因为 React Native 的组件和常见的 React 的组件极为类似,所以切换到 React Native相对是很容易的。

样式表

为了让渲染更容易、更加的有效,同样鼓励维护样式代码,React Native 实现了一个严格的 CSS 的子集。这同时也意味着开发者无需去学习特定平台的方法来从而设计视图,当然,开发者还是要花点时间去学习如何使用 React Native 的样式表的。

最大的不同就在于开发者不需要去担心特定的规则,因为样式的继承是被严重削弱过的,而且 React Native 使用的内联样式的语法。

这里是一个如何使用 React Native 来创建样式表的实例:

var styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30
}
});

然后,此种样式是由内联语法所应用的:

<View style={styles.container}>
...
</View>

语法是相当的容易阅读,但是如果开发者是来自于 Web 开发的背景的话,可能会发现有些不妥。(会有一个好的理由让你继续下去的,我保证!)即进一步去阅读关于 CSS,就会发现问题,若是以 React 的方法来解决它们的话,我这里强烈推荐 Christopher Chedeau 演示文稿:JS 中的 CSS

设置移动开发环境

React Native 的其中一个比较复杂的部分就是开发环境的设置。当基于 React Native 工作时,开发者针对移动开发需要所有的通常的工具,以及 JavaScript 编辑工具:一个文本编辑器,可能还需要 Chrome 的开发者调试工具。

对于 iOS 来说,这也就意味着需要开着 Xcode,以及 iOS 模拟器。对于 Android 来说,也需要开着的有 Android Studiom、将会用到的命令行工具。最终,开发者还需要运行着的 React Native 包,当然,开发者可以自行决定使用自己喜欢的文本编辑器来编辑 JavaScript 代码。

使用React Native来撰写跨平台的App

这么做的后果就是你需要在自己的本地开好多窗口,如此之多的工具,桌面杂乱到着实让人烦恼,但是,这样的 React Native 不会隐藏任何标准的移动开发过程。

跳转到原生代码

React Native 工作在已有平台的上 API 所提供的 JavaScript 接口上。在实践中,这意味着开发者可以撰写原先自己熟悉的 React 代码,React Native 的“桥”会自动完成相关的转换,但是若转换的功能不够完善没有彻底搞定的时候该如何处理?

不可避免的是,基于诸如 React Native 这样的新框架来开发的话,总会有一些 API 是不被支持的。若发生了这样的事情,你就需要撰写“本地的模块”来提供主机平台和所开发的 JavaScript 代码之间的通信。下面是一个关于 Objective-C 模块的 “Hello,World”的简易实例:

// Objective-C

#import "RCTBridgeModule.h"

@interface MyCustomModule : NSObject <RCTBridgeModule>
@end @implementation MyCustomModule RCT_EXPORT_MODULE(); // Available as NativeModules.MyCustomModule.processString
RCT_EXPORT_METHOD(processString:(NSString *)input callback:(RCTResponseSenderBlock)callback)
{
callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"]]);
}
@end

然后,要使用开发者本地的 JavaScript 模块的话,和需要其它的库是一样的:

// JavaScript

var React = require('react-native');
var { NativeModules, Text } = React; var Message = React.createClass({
getInitialState() {
return { text: 'Goodbye World.' };
},
componentDidMount() {
NativeModules.MyCustomModule.processString(this.state.text, (text) => {
this.setState({text});
});
},
render: function() {
return (
<Text>{this.state.text}</Text>
);
}
});

开发者这样做可能是因为所需要的 API 还没有得到支持,又或者是集成 React Native 组件到现有的 Objective-C 或 Java 代码中,又或者是需要写一些高性能的功能来处理一些密集的图形处理。非常值得庆幸的是,React Native 已经能够提供非常灵活的撰写和使用这些称之为“原生模块”,只要开发者需要,而且流程也是很顺溜的。哪怕是开发者从来没有 Objective-C 或 Java 的开发经验,撰写“桥”的代码是在和本地的移动开发相当舒服的情况下的一种很爽的体验。

部署应用程序

部署 React Native 的应用工作原理和传统的移动应用部署方式及其的类似,这并不是说这会非常的容易,众所周知,移动应用的发布流程是非常令人烦恼的。

要创建一个准备好部署的包,开发者需要使用打包好的 JavaScript 来代替在线重载的开发版本。在 iOS 中,这意味着需要在 AppDelegate.m 文件中更改一行代码,然后运行 react-native bundle --minify。对于 Android来说,开发者需要运行 ./gradlew assembleRelease,在这之后,打包的流程就和原来的移动开发过程没有任何的区别了,且最终的打包结果会提交到相关的应用程序商店中。

我的 flashcard 应用在 iOS 应用商店花了两周的时间,在 Google Play 商店花了不到一天的时间。换句话说,批准的时间完全符合我的预期,即和“传统的”移动应用没有任何区别,对于 React Native 所写的程序没有作任何的处罚。

使用React Native来撰写跨平台的App

有趣的是,苹果允许应用自行更新--因此绕过了令人头疼的部署过程--如果只是更新 JavaScript的话。微软最近释出了CodePush SDK,其允许 React Native 开发者立即更新。这是一个非常好的特性,我希望在接下来的几个月当中能够看到更多的应用程序享用此特性所带来的便捷。

结论和建议

如果你的背景是基于 web 开发的 JavaScript 工程师的话,我以为你会为 React Native 而高兴的。React Native 可以将任何背景的 web 开发者转变为潜在的移动开发者,而且对于现有的移动开发流程是得到了有效的改进。

当然,React Native 也不是完美无缺的,毕竟她还是一款较新的软件项目,要经受哪些不够成熟的程序库的折磨:有一些特性还是缺失的。而且最佳实践之类的还尚待挖掘。版本之间的变化太过于跳跃,虽然很少发生而且范围也比较有限,但还是偶尔会发生。

然而,React Native 已经足够的成熟,总而言之,好处是远远大于瑕疵的。基于 React Native,开发者可以使用单一的 JavaScript 代码库来创建 iOS 和 Android 的应用,而且还没有质量和性能上的损耗。即使开发者没有 JavaScript 的背景,也很难不被更加快速的开发周期和几乎完全的代码重用这样的好处所吸引。而且因为 React Native 允许开发者在需要的时候切换到“常见”的开发状态,你还不会受限于框架。总而言之,React Native 交付了一款高质量的、跨平台的移动开发工具,如果读者你要加入到移动开发的项目中来,你需要慎重的考虑下使用 React Native。

如果你想更进一步的阅读,我已经出版了一本专门、详细的图书:《React Native 学习》,电子版和纸版均有售卖,可以到奥姆莱 和亚马逊上找到。你也可以在Twitter上关注我,我的账号是:@brindellev,我非常期望听到大家使用 React Native 的经历以及疑问。

国内:

Facebook在3.26 F8大会上开源了React Native,本文是对React Native的技术背景、规划和风险的概述。

组里的同学于4.2完成了天猫iPad客户端“猜你喜欢”业务的React Native改造(4月中发版)。本周开始陆续放出性能/体验、稳定性、扩展性、开发效率等评估结果。

使用React Native来撰写跨平台的App
图1 - 4.2已完成React Native改造的业务

背景

为什么需要 React Native

What we really want is the user experience of the native mobile platforms, combined with thedeveloper experience we have when building with React on the web.

摘自3.26 React Native的发布稿,加粗的关键字传达了React Native的设计理念:既拥有Native的用户体验、又保留React的开发效率。这个理念似乎迎合了业界普片存在的痛点,开源不到1周github star破万,目前是11000+。

使用React Native来撰写跨平台的App
图1 - React Native github

React Native项目成员Tom Occhino发表的React Native: Bringing modern web techniques to mobile详细描述了React Native的设计理念。Occhino认为尽管Native开发成本更高,但现阶段Native仍然是必须的,因为Web的用户体验仍无法超越Native:

  1. Native的原生控件有更好的体验;
  2. Native有更好的手势识别;
  3. Native有更合适的线程模型,尽管Web Worker可以解决一部分问题,但如图像解码、文本渲染仍无法多线程渲染,这影响了Web的流畅性。

Occhino没提到的还有Native能实现更丰富细腻的动画效果,归根结底是现阶段Native具有更好的人机交互体验。笔者认为这些例子是有说服力的,也是React Native出现的直接原因。

使用React Native来撰写跨平台的App
图2 - Occhino在F8分享了React Native(Keynote)

“Learn once, write anywhere”

“Learn once, write anywhere”同样出自Occhino的文章。因为不同Native平台上的用户体验是不同的,React Native不强求一份原生代码支持多个平台,所以不提“Write once, run anywhere”(Java),提出了“Learn once, write anywhere”。

使用React Native来撰写跨平台的App
图3 - “Learn once, write anywhere”

这张图是笔者根据理解画的一张示意图,自下而上依次是:

  1. React:不同平台上编写基于React的代码,“Learn once, write anywhere”。
  2. Virtual DOM:相对Browser环境下的DOM(文档对象模型)而言,Virtual DOM是DOM在内存中的一种轻量级表达方式(原话是lightweight representation of the document),可以通过不同的渲染引擎生成不同平台下的UI,JS和Native之间通过Bridge通信
  3. Web/iOS/Android:已实现了Web和iOS平台,Android平台预计将于2015年10月实现

前文多处提到的React是Facebook 2013年开源的Web开发框架,笔者在翻阅其2013年发布稿时,发现这么一段:

使用React Native来撰写跨平台的App
图4 - 摘自React发布稿(2013)

  1. 加亮文字显示2013年已经在开发React Native的原型,现在也算是厚积薄发了。
  2. 最近另一个比较火的项目是Flipboard React Canvas(详见 @rank),渲染层使用了Web Canvas来提升交互流畅性,这和上图第一个尝试类似。

React本身也是个庞大的话题不再展开,详见React wiki

笔者认为“Write once, run anywhere”对提升效率仍然是必要的,并且和“Learn once, write anywhere”也没有冲突,我们内部正在改造已有的组件库和HybridAPI,让其适配(补齐)React Native的组件,从而写一份代码可以运行在iOS和Web上,待成熟后开源出来。

规划

下图展示了业务和技术为React Native所做的改造:

使用React Native来撰写跨平台的App
图5 - 业务和技术改造

自下而上:

  1. React Node:React支持服务端渲染,通常用于首屏服务端渲染;典型场景是多页列表,首屏服务端渲染翻页客户端渲染,避免首次请求页面时发起2次http请求。
  2. React Native基础环境:
    1. Framework集成:尽管React Native放出了Integration with Existing App文档,集成到现有复杂App中仍然会遇到很多细节问题,比如集成到天猫iPad客户端就花了组里iOS同学2天的时间。
    2. Networking改造:主要是重新建立session,而session通常存放于http header cookie中,React Native提供的网络IO fetch和XMLHttpRequest不支持改写cookie。所以要不在保证安全的条件下实现fetch的扩展,要么由native负责网络IO(已有session机制)再通过HybridAPI由JS调用,暂时选择了后者。
    3. 缓存/打包方案:只要有资源从服务器端加载就避免不了这个话题,React Native也是如此,缓存用于解决资源二次访问时的加载性能,打包解决的是资源首次访问时的加载性能。
  3. MUI是一套组件库,目前会采用向React Native组件补齐的思路进行改造。
  4. HybridAPI是阿里一组Hybrid API,此前也在多个公开场合分享过不再累述,React Native建立了自己的通信机制@bang),看起来更高效(未验证),改造成本不大。
  5. 最快的一个业务将于4月中上线,通过最初几个业务改造推动整体系统的改造,如果效果如预期则会启动更大规模的业务改造。

更多详细规划和进展,以及性能、稳定性、扩展性的数据随后放出。

风险

  1. 尽管Facebook有3款App(Groups、Ads Manager、F8)使用了React Native,随着React Native大规模应用,Appstore的政策是否有变不得而知,我们只能往前走一步。
  2. React Native Android 预计2015年10月才发布,这对希望三端(Web/iOS/Android)架构一致的用户而言也算个风险。
  3. iOS6 JavascriptCore为私有API,如在iOS6上使用有拒审风险。
  4. ListView 性能问题需要持续关注(react-native github issue)

React Native相对于Webview和Native的优势和劣势 @berg 也给出了较详细的描述,可以相互参照。