版权声明:本文原创发布于公众号 wingjay,转载请务必注明出处! https://www.jianshu.com/p/b88944250b25
React Native 诞生于 2015 年,名副其实的富二代,主要使命是为父出征,与 Apple 和 Google 抗衡,为开发者带去一套跨平台、动态更新的 Javascript 框架,口号是:Learn once, write anywhere:Build mobile apps with React。在试图推翻 Android 和 iOS 压制的同时,还提携了一把自家兄弟:React。
从诞生之日 React Native 就充满了期待和争议。期待是无数开发者希望不用忍受频繁发版的噩梦,也不用同时为两个平台开发业务逻辑几无差别的两个 App;争议是 React Native 真的能以一己之力救大众于水火吗?React Native 在跨平台时还能保持良好的用户体验吗?
当然我们知道,这种问题向来都是仁者见仁,智者见智。比起一味的疑惑、争论,还不如来好好看看这货究竟是个啥?甚至自己动手来玩一把。
本文主要针对两类读者:
下面我们来看下 Hybrid 及 React Native 等开发模式包含了哪些常规移动开发所不具备的优势。
传统的客户端开发模式是怎样的呢?
Android 与 iOS Team 分别编写客户端代码,打包,分发到 Play Store 和 Apple Store,通过更新 JSON 数据来更新页面。
不过,当客户端发生严重问题而服务器上无法 quick fix 时,就不得不重新发版。
对国外 Android 市场而言还好,因为能通过 Play Store 快速更新;国内 Android 市场则由于分发渠道太杂,很难及时把新版本立即推送给所有用户,当然这也是为何热修复技术在国内盛行而国外冷清的原因;而 Apple Store 则需要一定的审核时间,而且最近又在抓 iOS 热修复框架如 JsPatch、Rollout 等。
相比而言,Hybrid 和 RN/Weex 模式除了能下发 Json 数据来刷新界面内容,更能直接下发业务逻辑代码,直接实现整体 App 的更新。而且,它们不用在乎 Android 和 iOS 两个平台,因为一份 JS 代码写好后,把 JS Bundle 放在服务器上,所有的客户端立即更新。
一般而言,同一款产品的 Android 和 iOS 两端除 UI 有些许不同外,多数业务逻辑几乎完全一致,这便造成了人力的浪费。
而最近 Instagram 的官博 React Native at Instagram 一文中已经提到,利用 RN (React Native 缩写,下同) 开发的 feature 可以实现 85% - 99%
的代码复用率。这意味着我们可以用更少的人力成本来达到相同的效果。
实现上面的效果有两种开发框架:混合开发框架 Cordova 和基于 Javascript 的 React-Native、Weex 框架。
下面我从自己的实践经验出发做些比较,也欢迎读者提出自己看法。
最开始觉得 RN 的学习成本比较大,所以首先考虑了 Weex 框架,据说是阿里巴巴良心出品。不过在尝试后不得不选择了放弃,原因有这几点:
最基本
的 ListView,在 iOS 运行良好,而同样的 Demo 代码到了 Android 这边的下拉刷新
就出现了问题,这使得我们开始警惕;当然,我是很希望国内也能推出优质的开源项目来和国外大厂抗衡的,不过真正优质的大型开源项目往往除了开发者的个人能力,和公司的战略和制度关系也很大。
这里的 Hybrid 开发主要针对 Cordova 框架,其实在放弃 Weex 之后我们还是没考虑 RN,而是转过去了解 Cordova,不过做了大致了解后也放弃了。主要硬伤有两点:
性能和用户体验是移动 App 的命根子。
因此,综合考虑下来,我们还是决定相信 Facebook 并采用 RN。
上面我提到了 RN 的一些优势,不过作为开发者更加需要明确其劣势,我总结了下大概有以下几点劣势:
split apk
的技术就能缩小到到 1MB 左右的增幅。对于一个用 RN 搭建的移动 App,在启动后会从服务器下载最新的 JS Bundle 文件,然后由本地 JavascriptCore 引擎对 JS 文件进行解析,并利用 Bridge 映射到对应的 Native 方法和 UI 控件。得到的效果是:
首先 IDE 方面,RN 推荐了一些工具:
本人的话目前采用的是 Sublime
,因为个人常用 Sublime,而且第三方插件很丰富,轻量方便。下面简单说下配置,感兴趣的小伙伴可以看下。
当然,用 Atom 的小伙伴自然要首先考虑 Nuclide。
引入 RN 有两种方法:从零构建;集成到已有项目。
先说第一种,从零开始构建,比较简单,遵循官方文档 Getting Started 基于你自己的操作系统和平台一步步安装相关的依赖,然后利用如下命令:
react-native init AwesomeProject
你就创建好一个 RN 工程项目了,结构如下:
里面有四个文件夹:
android
/ ios
:各自存放了一个相关平台的工程 project,可以直接下拉 JS Bundle 并运行,对于移动端小白而言可以不用管里面的具体实现;node_modules
:里面是自动生成的 node
依赖之类的文件,通过读取 package.json
里的配置来生成;js
:这个文件夹最为重要,我们的开发都在这个文件夹里,把写好的 js
文件打包下发给 client 就会自动生效。有很多公司是希望在现有 App 的基础上集成 RN
来开发一些特定的 Feature,这种情况就不能参考上面的方法了。在 RN 的官方文档里有一节 Integration with Existing Apps , 只需要按照一步步做即可。
以 Android 为例,大概要做以下几步:
gradle
依赖:compile "com.facebook.react:react-native:+" // From node_modules.
;Activity
,指定 JS bundle
和入口 Component
名字即会自动在这个 Activity
里去加载 JS bundle
文件;总之需要说明的是,即使是移动端小白,也可以遵循文档里的指示完成这一步。接下来的大部分时间只要关心 JS 端开发就行了。
我们知道 RN 采用了 React 和 ES6 的语法,所以我们必须先对这些语法有一定了解才能去读 RN 的代码。
关于 Javascript
,我推荐 W3School 里的 JS语法 和 MDN 里的 JS手册,大家只要对一些基础语法做些了解就可以。
关于 React
,我推荐 阮一峰 写的 React 入门实例教程,基本上把文章读一遍,再自己动手写一遍,就能理会到 React 的大致用法了。
关于 ES6
、ES7
、JSX
等,感兴趣的可以看一下 RN 文档中 Javascript Environment 里提到的支持的方法,需要时再来查询也可以。也可以看 Babel
出的 Learn ES2015 手册。
这里有一个很不错的 GitHub 项目,帮助你通过交互性的例子来快速上手语法知识:React Native Express。
简单熟悉了 React
语法后,基本能正常阅读 RN 的示例代码了。
正式开发 App 的第一步当然就是写 UI 界面了,由于 RN 已经封装好了一套 JS 的 UI 组件,这些组件会自动在 Android/iOS 端调用对应的原生 UI 组件,因此我们只需要熟悉这些 UI 组件的用法及属性、回调方法即可。
我们可以在文档的 Components 看到不少组件,比如View, Text, Button, Image, Switch
, 还有我们用的最多的 ScrollView
和 ListView
。
在读文档时,我们可以先通过一边写代码一边读文档的方式进行,RN 非常贴心,直接在 Web 里嵌入了模拟器,我们只要修改编辑框里的代码,立即就能在右边的模拟器看到效果。这极大的降低了我们的学习成本。
另外,在学习一个组件时,我们要区分哪个属性是某个平台特有的。比如下面两个 Text 的属性:textBreakStrategy
只会在 Android 上生效,而 adjustsFontSizeToFit
只可以用在 iOS 上。
然后,如果你希望在 Android 和 iOS 里显示不同的内容怎么办呢?RN 里有一节是Platform Specific Code,可以有如下几种形式来进行区分:
if (Platform.OS === 'ios') {
// stuff for ios
} else {
// stuff for android
}
除此之外,UI 组件的用法学习就很类似常规的 Html 标签了,只要知道其使用方式即可,甚至需要用的时候再来查文档也行。
学完上面的我们已经能够写出 UI 界面了,而且这套界面已经能够在不同平台上转化成各自平台的 Native UI 了。然后,我们就需要去网络层请求真实数据了。
RN 里提供了 Fetch API 来进行实现。举个例子,你想要通过 GET 方法去请求数据并转化成 JSON,可以通过如下代码实现:
fetch('https://facebook.github.io/react-native/movies.json')
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
熟悉 Reactive 编程的伙伴应该对这样的语法不陌生,比如 Android 上的 RxJava; iOS 上的 RxSwift;Web 上的 RxJS。上面 function 的功能就是:请求网址 https://facebook.github.io/react-native/movies.json
,把返回的 Response 转化成 JSON object,取出 JSON object 里的 movies
字段。同时,如果发生 error 会被 catch 住。
当然,上面是最基本的 GET 请求,Fetch API 还支持自定义 Headers,更换 Method,添加 Body 等。
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
})
})
上面构建了一个基本的 POST 请求,添加了自己的 Headers:Accept
和Content-Type
,添加了 Body。
因此看下来,RN 里的网络请求不仅具备了 Reactive 编程的简洁,也能自定义常规的 Http 请求,写法简单。
除了 Fetch API 之外,RN 还内置了 XMLHttpRequest API(俗称 AJAX),而且支持TCP 全双工通信方式 WebSocket。
调试是很多程序员非常关注的一个环节,因为 RN 是用 JS 写完后到 Native 解释成 Native 方法来执行的,因此如果能快速调试 JS 代码是非常重要的一环。
最开始 RN 的调试功能比较弱,不过现在的 Debugging 功能在我看来还是很不错的。一般来讲可以有以下几个调试方式:
RN 里默认集成了 In-App 的错误提示方式,即在 App 运行过程中会弹出全屏的报错信息呈现给你,而你也可以通过阅读具体的错误信息快速找到错误原因。通过点击这个错误信息里的某一行,会立即自动打开对应的代码。
在开发 Client 时,我们一般都会用 Log.log()
来打印一些运行时变量的值,然后实时查看打印出来的 log 来调试,在 RN 也一样,你只要在 JS 里写一句 console.log('this is log data')
,就会自动在 Client 的常规 log 里能看到,比如 Android 的 adb logcat
里就会自动打印出'this is log data'
一行。
这个杀器的最牛逼之处就是可以像 Client 一样,逐行调试代码!
我们来看下面一张图。从左往右。先是文件目录,我们选中了 index.js
文件夹,然后第二个 Tab,是 index.js
的内容。这里关键的是我可以直接选中某一行代码设断点。当 Client 运行到这一行时,就会在第三个 Tab 里打印出运行时环境及变量。我们可以看到 props
里就有我们传进去的变量值。
有了以上几种调试方式,我们几乎可以和常规的 Native 开发一样来调试 RN 代码了,不得不说 RN Team 确实牛 x 啊!
这又是另一个牛 x 之处啊。
很多人觉得 RN 限制太多,只能支持有限的 View 组件和有限的方法,难以发挥 Client 的最大性能。简单点说,在 Client 可以绘制复杂的 View,可以调用高性能 C++ 等底层代码,但 RN 却做不到。
于是,RN 里提出了 Native Modules
和 Native UI Component
两种技术。
所谓 Native Modules,就是自己在 Client 写好了某些方法,由于某些原因这些方法不太方便或者无法搬到 RN 里面,那么,我们可以在 Client 把这些方法暴露出来给 RN,然后在 JS 里可以像 import 普通的 module 一样把这些 Native Modules 引入进去,直接调用。
具体的实现方法可以参考文档 iOS Native Modules 和 Android Native Modules。
很多时候我们在写 Client 时,为了实现 Designer 天马行空的设计,常常需要自定义 View,即自己绘制某些系统并不提供的特定 UI。可想而知,这些 View 肯定不会出现在 RN 的 UI Component 里。
那么,我们就需要首先在 Native 层自己写好一个自定义 View,然后利用Native UI Component
技术把这个 View 及其中某些 public 方法暴露给 RN,那么 RN 就能直接 import 进来并显示了。
具体的实现方法可以参考文档 iOS Native UI Component 和 Android Native UI Component。
如果读过文档不是很理解的小伙伴可以留言,我再 post 一些 demo 代码上来
这里借鉴下前段时间旧金山的 React Native 会议上的一些优劣总结给读者以参考。当然不一定对,仅供参考。
RN 的优点:
RN 的缺点:
适合下面这些人/公司:
下面这些人要稍微考虑下:
几个月前我对 React Native 也非常不看好,当然现在也没有说非常看好。或者说,写这篇文章毫无为 React Native 布道之意。
接触 React Native 主要是因为业务需要,PM 希望能够随时改动某块变化较大的模块,常规的开发提交流程往往需要较长的时间,而热修复技术本身并未得到 Google 和 Apple 的官方认可,也就是随时可能因破坏生态安全之名被取缔。
因此才考虑去了解 Hybrid 开发和 JS Native 开发模式,在了解过程中,又由于性能差、用户体验不好而放弃 Hybrid,由于社区不完善、Bug 较多等原因放弃 Weex,最终才选择了 React Native,开始学习 React、JSX等语法。
目前使用下来对 React Native 的一些个人感受:
6MB
左右的 size 增幅,不过在采用了 split apk
后就只有 1MB
左右增幅。关于React Native一直以来都有很多争议。
不过我想说的是,React Native 所代表的跨平台、动态更新技术已经引起了全世界开发者关注,而且这种技术势必会是未来的需求和潮流。React Native 不一定会成功,但至少目前 React Native 已经是这一领域的领跑者。
而写这篇文章的目的,就是希望告诉更多开发者,React Native 并不完美,但值得一试。
谢谢。
wingjay