目前React Native最好用的跨平台tab bar tabnavigatorconfig是哪个

Facebook 于日推出react native for Android 版本, 加上2014年底已经开源的IOS版本,至此RN (react-native)真正成为跨平台的客户端框架。本篇主要是从分析代码入手,探讨一下RN在安卓平台上是如何构建一套JS的运行框架。
一、 整体架构
RN 这套框架让 JS开发者可以大部分使用JS代码就可以构建一个跨平台APP。 Faceb
Facebook 于日推出react native for Android 版本, 加上2014年底已经开源的IOS版本,至此RN (react-native)真正成为跨平台的客户端框架。本篇主要是从分析代码入手,探讨一下RN在安卓平台上是如何构建一套JS的运行框架。
一、 整体架构
RN 这套框架让 JS开发者可以大部分使用JS代码就可以构建一个跨平台APP。 Facebook官方说法是learn once, run everywhere, 即在Android 、 IOS、 Browser各个平台,程序画UI和写逻辑的方式都大致相同。因为JS 可以动态加载,从而理论上可以做到write once, run everywhere, 当然要做额外的适配处理。如图:
RN需要一个JS的运行环境, 在IOS上直接使用内置的javascriptcore, 在Android 则使用webkit.org官方开源的jsc.so。 此外还集成了其他开源组件,如fresco图片组件,okhttp网络组件等。
RN 会把应用的JS代码(包括依赖的framework)编译成一个js文件(一般命名为index.android.bundle), , RN的整体框架目标就是为了解释运行这个js 脚本文件,如果是js 扩展的API, 则直接通过bridge调用native方法; 如果是UI界面, 则映射到virtual DOM这个虚拟的JS数据结构中,通过bridge 传递到native , 然后根据数据属性设置各个对应的真实native的View。 bridge是一种JS 和 JAVA代码通信的机制, 用bridge函数传入对方module 和 method即可得到异步回调的结果。
对于JS开发者来说, 画UI只需要画到virtual DOM 中,不需要特别关心具体的平台, 还是原来的单线程开发,还是原来HTML 组装UI(JSX),还是原来的样式模型(部分兼容 )。RN的界面处理除了实现View 增删改查的接口之外,还自定义一套样式表达CSSLayout,这套CSSLayout也是跨平台实现。 RN 拥有画UI的跨平台能力,主要是加入Virtual DOM编程模型,该方法一方面可以照顾到JS开发者在html DOM的部分传承, 让JS 开发者可以用类似DOM编程模型就可以开发原生APP , 另一方面则可以让Virtual DOM适配实现到各个平台,实现跨平台的能力,并且为未来增加更多的想象空间, 比如react-cavas, react-openGL。而实际上react-native也是从react-js演变而来。
对于 Android 开发者来说, RN是一个普通的安卓程序加上一堆事件响应, 事件来源主要是JS的命令。主要有二个线程,UI main thread, JS thread。 UI thread创建一个APP的事件循环后,就挂在looper等待事件 , 事件驱动各自的对象执行命令。 JS thread 运行的脚本相当于底层数据采集器, 不断上传数据,转化成UI 事件, 通过bridge转发到UI thread, 从而改变真实的View。 后面再深一层发现, UI main thread 跟 JS thread更像是CS 模型,JS thread更像服务端, UI main thread是客户端, UI main thread 不断询问JS thread并且请求数据,如果数据有变,则更新UI界面。
二、 代码流程
对于JS开发者来说, 整个RN APP就只有一个JS文件, 而开发者需要编写的就只有如上部分。主要是四个部分:
require 所有依赖到的组件, 相当于java中的import 或者 c++ 中的include。
var AwesomeProject = React.createClass 创建APP, 并且在render函数中返回UI界面结构(采用JSX ), 实际经过编译, 都会变成JS 代码, 比如 变成 React.createElement(View,{style:{flex:1}},
var styles = StyleSheet.create({, 创建CSS 样式,实际上会直接当做参数直接反馈到上面的React.createElement
AppRegistry.registerComponent('AwesomeProject', () =& AwesomeProject); 以上三个更像是参数,这个才是JS 程序的入口。即把当前APP的对象注册到AppRegistry组件中, AppRegistry组件是js module。
接着就等待Native事件驱动渲染JS端定义的APP组件。
2、Native 入口
对于Android 开发者, 普通安卓程序入口是Activity.onCreate()方法 , 主要有三个对象
ReactRootView, Android 标准的FrameLayout对象,另外一个功能是提供react 世界的入口,函数startReactApplication实际调用attachMeasuredRootView触发react世界的初始化。
MyReactPackage, 配置当前APP 需要加载的模块,RN 的JS框架会在初始化阶段就会把native的模块按照配置加载到JS数据结构中(MessageQueue), 从而才能在JS 层即可直接判断native是否支持某个模块。支持三种类型模块配置, native module(实际就是不需要操作View结构的API), view managers(实际是映射到virtual DOM中的View组件), JS module 。
ReactInstanceManager, 构建React世界的运行环境,发送事件到JS世界, 驱动整个React世界运转。 通过builder可以创建不同的React环境, 比如内置js 路径, 开发环境dev的js名字,是否支持调试等。doInBackground会加载指定的JS文件, onPostExecute会调用runApplication接口运行JS APP。
ReactRootView第一次onMeasured计算完成, 然后会利用ReactInstanceManager创建 ReactContext上下文环境。重要的是初始化bridge以及加载js文件, 利用JSBundleLoader方法加载index.android.bundle. 如图
此刻进入JS 世界, 开发者的js 语句连同react js框架层被执行。该步骤最终语句是执行AppRegistry.registerComponent注册一个APP组件,但还没有到开始渲染。
当运行环境准备完毕, 则调用bridge方法运行上步注册的APP组件,触发一连串JS 和 Native相互通信,配合事件驱动, 从而完成native世界的渲染。如图利用bridge方法运行上面注册的JS APP组件的runApplication方法:
3、事件循环
所有的APP在操作系统中, 最终都会使用一个事件循环来运行。
一般来说,JS 开发者只需要开发各个组件对象,监听组件事件, 然后利用framework接口调用render方法渲染组件。
而实际上,JS 也是单线程事件循环,不管是 API调用, virtural DOM同步, 还是系统事件监听, 都是异步事件,采用Observer(观察者)模式监听JAVA层事件, JAVA层会把JS 关心的事件通过bridge直接使用javascriptCore的接口执行固定的脚本, 比如"requrire (test_module).test_methode(test_args)"。此时,UI main thread相当于work thread, 把系统事件或者用户事件往JS层抛,同时,JS 层也不断调用模块API或者UI组件 , 驱动JAVA层完成实际的View渲染。JS开发者只需要监听JS层framework定义的事件即可。如图即JS thread 的消息队列循环:
分析代码可知,消息线程创建于ReactContext环境初始化时, MessageQueueThread.java当中, 该消息队列主要接收系统事件(如 Vsync、timer、doFrame、backkey)、UI事件(如键盘弹起、滚动等)以及 callback事件(JS 的回调函数)。如图即ReactRootView往JS 传递键盘弹出的事件:
而对于Android 开发者, Android 已经为APP创建一个默认的 Main Looper, 不管是Android System 还是JS 事件都是发送到Main thread通过UI渲染出来。如图即是MessageQueueThread.java直接使用主线程Looper。
跟普通APP不同是,此时JS thread相当于work thread, JS会把对应的事件或者数据通过bridge发送到UI thread。 如图即是native Java层收到的JS事件的处理函数:
三、 通信机制
RN框架最主要的就是实现了一套JAVA和 JS通信的方案,该方案可以做到比较简便的互调对方的接口。一般的JS运行环境是直接扩展JS接口,然后JS通过扩展接口发送信息到主线程。但RN的通信的实现机制是单向调用,Native线程定期向JS线程拉取数据, 然后转成JS的调用预期,最后转交给Native对应的调用模块。这样最终同样也可以达到Java和 JS 定义的Module互相调用的目的。
1、JS调用java
JS调用java 使用通过扩展模块require('NativeModules')获取native模块,然后直接调用native公开的方法,比如require('NativeModules').UIManager.manageChildren()。 JS 调用require('NativeModules')实际上是获取MessageQueue里面的一个native模块列表的属性, 如:
使用_genModules 加载所有native module到 RemoteModules数组。RemoteModules每项都是一个映射到native module的JS对象。
调用RemoteModules 的方法, 实际是把moduleID、methodId、args放入三个queue保存。
至此, JS端调用完毕, queue中数据要等待Native层通过bridge来取。
native层会在一定条件下触发事件, 通过bridge调用callFunctionReturnFlushedQueue和 invokeCallbackAndReturnFlushedQueue ,得到的返回值就是这三个queue。
bridge会把这三个queue交给parseMethodCalls解析, 然后通过JNI回调函数转发到Java层
m_callback 函数是在bridge初始化的时候设置到c++层, 如:
然后在回调函数中,陆续调用ReactCallback对象的call方法,weakCallback就是java层初始化bridge时传入的NativeModulesReactCallback对象,也就是ReactCallback的子类。
到此,转入Java层. 从native module配置表中,取到对应module和method,并执行。
2、java调用JS
之前ReactInstanceManager 中运行JS APP组件,JAVA 是调用catalystInstance.getJSModule 方法获取JS 对象,然后直接访问对象方法runApplication。实际上getJSModule 返回的是js对象在java层的映射对象。
java层可以调用的JS模块主要在CoreModulesPackage.createJSModules方法配置,有:
如果调用JSModules对象的方法,则会动态代理跳转到(mBridge).callFunction(moduleId, methodId, arguments);
接着调用ReactBridge中声明的JNI 函数,public native void callFunction(int moduleId, int methodId, NativeArray arguments);
通过JS 的require和 apply函数拼接一段JS 代码, 然后用javascriptCore的脚本运行接口执行,并得到返回值。
这样就在JS引擎中运行了一段JS代码并得到返回值,实现了JAVA层到JS层的调用。每次有JAVA对JS的访问, 则在返回值中从JS层的messageQueue.js中抓取之前累积的一堆JS calls。因为JAVA层要把时间同步、 系统帧绘制等事件传递给JS, 因此queue中的JS calls都会在很短的时间内被抓取。
四、 扩展机制
1、 模块扩展(native module)官方文档操作:
2、 组件扩展(UI component)官方文档操作:
因为react模块加载主要在ReactPackage类配置,因此扩展可以通过反射、外部依赖注入等机制,可以做到跟H5容器一样实现动态插拔的插件式扩展。比如API扩展, 通过外部传入扩展模块的类名即可反射构造函数创建新的API:
public List&NativeModule& createNativeModules(ReactApplicationContext reactContext) ;
java.lang.reflect.Constructor constructor = c.getConstructor(parameterTypes);
Object[] parameters = ;
NativeModule module = (NativeModule) constructor.newInstance(parameters);
modules.add(module);
}catch (Exception e)
五、 离线加载
离线包支持。 目前RN官方支持内置APK打包以及dev server在线更新。而实际上,一般的容器都会实现一套离线包发布平台。大致的实现方案是自定义一个JSBundleLoader,对接到应用管理发布平台。
分离react 框架代码和应用业务代码。目前官方的生产工具是把框架代码和业务代码弄成一个bundle。 但框架代码很大,需要共用, 因此要分离出框架代码单独前置加载。 应用业务代码变成很小一段JS代码单独发布。如果每次都加载框架代码, 启动业务代码会比较慢,一个helloworld都需要4秒左右。初步实践方案是把ReactInstanceManager设置成全局变量共享,在Native APP 启动初始化或者第一次进入RN APP时初始化ReactInstanceManager。这个可能会导致多个RN APP全局变量冲突。
在线更新离线包更新主要依赖应用管理发布平台,大致可以做到跟H5离线包一致。
一般说的是图片资源比较多, RN 使用控件显示图片,如:
通过source属性设置图片资源路径, 映射到native层:
因此不管是离线包内资源还是系统资源,只要能转换成Android 统一资源定位URI对象,即可获取到图片。
如果是静态资源,则直接URI统一定位。如果是动态资源, 比如要通过网关获取到base64格式的图片,则需要native扩展特别接口。
1、 可能瓶颈
因为bridge,
JS和 JAVA是异步互通,如果实现复杂多API的逻辑,可能会导致部分效率损耗在多线程通信。JS 异步的编程方式多多少少带来一些不便。
因为bridge,
可能某些场景做不到及时响应。比如帧动画的实时控制。
Android版本刚推出不完善,并且目前RN版本还在不停的更新中, 可能存在暗坑。
加入JS引擎, 内存的控制比较麻烦,会比普通native增加不少。
2、 待研究
动态注入的API插件实现方案,能跟h5容器共用实现。
因为RN已经具备很多的灵活, JS也可以做到很多大型控件,所以native UI扩展需要定义JS 和 native边界, 哪些是JS 实现, 哪些是native实现。
动画的实现方式。
H5容器和RN容器融合方案
write once, 完全跨平台。
JS 层支持 Fragment manager
性能比较数据
Demo还在实现当中,等抓完再补充。
如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@ 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】浅析混合云和跨地域网络构建实践,分享高性能负载均衡设计,9月21日阿里云专家和你说说网络那些事儿,足不出户看直播,赶紧预约吧!&&
为企业和开发者提供稳定、安全、智能的把网站域名或应用资源转换为计算机用于互连的数字 IP地址,从而将最终用户的访...
提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过Docker容器来进行应用生命周期管理。
HTTPDNS是面向移动开发者推出的一款域名解析产品,具有域名防劫持、精准调度的特性。
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
2017杭州云栖大会火热抢票
Loading...如何用React Native编写跨平台APP
用React Native编写跨平台APP
React Native 是一个编写iOS与平台实时、原生渲染的应用程序的框架。它基于React,Facebook的Script的库,为的是构建用户接口,而并不是以为目标,它是以手机平台为目的。换句话说,如果你是一个web开发者,你可以使用React Native去编写干净、快速的移动APP,并且可以体会到熟悉的框架和单个的的代码库。
在这之前,我们已经听到用诸如Cordova或者是Titanium这些框架的APP开发的承诺,那么使用React Native去开发APP究竟如何呢?在这篇文章中,我们将会探索React Native是什么,以及它究竟是如何工作的。然后,我们将会报道使用React Native去编写iOS以及Android的应用程序究竟是如何的,结尾,你讲会看到为什么说React Native是你的接下来的一个移动项目开发极其推荐的一个选择。
什么是React Native?
在我们准备重点介绍开发者的经验之前,我们先来说明一下React Native是什么,以及略微设计一下React Native是如何工作的。
它仅仅是React
React Native是一个为了编写用户交互接口的JavaScript的库。通常是用于web上。由Facebook开发,并于2013年开源。React已经见证了广泛的使用。React在领域内的使用相对来说是狭窄的,它仅仅关注它自己,通过渲染你的应用程序的用户交互的接口。与一般的大型的MVC风格的库呈分庭抗礼的局面。
开发者因为一系列的原因,对于React已经是趋之若鹜了。它是轻量级的,同时它提供了令人为之眼前一亮的行为,尤其是对于快速改变的数据。同时由于其自身的组件的结构,它鼓励你去写更加具有模块化的,可复用的代码。
要不是移动端,React Native果真仅仅是React。这里是有一些差别:你应当使用一个View组件而不是一个Div组件,应当使用一个Image标签,而不是img标签,开发者的绝大多数都是保持差不多相同的,当然了,如果具备一定的Objective-c或者是Java的语言知识,那就更好了。对于移动开发者,会有诸如这些机警的考虑(我是否做了多机型的适配工作?我的触摸点是否已经设置了足够大?)然而,React Native将会感觉完全熟悉,并且是舒适的,当然这是就那些已经学会如何在浏览器中用React做开发的开发者而言。
它确实是React
首当其冲,让人对React Native产生惊艳的感觉的是React Native的的确确是Native的。其余的针对移动端的JavaScript引擎是打包你的JavaScript代码进入到一个优化过的WebView当中,也许这些WebView也实现了某些本地化的UI的行为的接口,比如说动画,但是归根到底,你依旧写的是一个web app。
在React中,一个组件描述了自己的特性;React为你处理渲染。一个干净的抽象层将这两个函数隔绝开来。对于web端,为了渲染组件,React使用了标准的Html的标签,这个相同的抽象层,一般被理解为&Bridge&,能够使得React Native去触发iOS与Android上面的渲染的API,那就意味着,在iOS,你的React Native组件绘制真正的UI View,然而在Android上,它们将会绘制native View。
你将会写一些看上去比较糟糕,与标准的JavaScript、CSS、Html比较相像的代码。并非直接是向下编译成本地代码,React Native 拿到你的应用程序,使用宿主平台的JavaScript引擎运行它,同时不阻塞主UI主线程,这样,你将会得到原生的app的操作、动画、体验的效果,而不需要必须写成Objective-C或者Java形式的代码。其他的跨平台的app的开发的模式,比如说,CZ喎"/kf/ware/vc/" target="_blank" class="keylink">vcmRvdmG78tXfysdUaXRhbml1baOsvvi21LTvsru1vdPr1K3J+szl0em78tXfse3P1tXi1ta437bI0rvWwrXE0Ke5+6GjPC9wPg0KPC9ibG9ja3F1b3RlPg0KPGgzIGlkPQ=="一个比较好的开发者的体验">一个比较好的开发者的体验
与标准的Android或者是iOS的开发相比,React Native提供了一种更加具有震撼效果的开发者的体验,因为你的应用程序几乎全部是基于JavaScript开发的,你将会获取到一系列的Web开发上面的优势,比如能够瞬间刷新你的应用程序去看到你的代码的改变。与花费长时间等待构建一个传统的移动app相比,React Native感觉简直就是上天的馈赠。
处理多个平台
令人充满感激的是,React Native支持多个平台. React Native的绝大多数的API都是跨平台的, 所以你仅仅需要编写一个React Native组件, 它将会无缝的工作于iOS与Android平台上。Facebook宣传它们的Ad Manager 应用程序 87%代码在两个平台重用, 然而我写了一个flashcard 应用程序更是没有一点平台相关的代码。如果你确实希望用到一些平台相关的代码&由于iOS与Android的两个平台的不同的交互,比如,或者是你希望重复利用一个平台的API的优势&那也是很容易的,React Native支持你去指定特定的平台的版本,对于每一个组件而言,然后,你可以将他们集成到你的React Native版本的应用程序中。
用React Native来进行开发
使用一个简单的JavaScript的代码库来进行写真正的iOS或者是Android的native 应用看上去是一件没有头脑的事情。那么用React Native来进行工作究竟是怎样的呢?
去开始开发React Native的应用程序,除了React Native以外,还需要安装一些针对iOS以及Android平台的一般性的依赖的组件。具体的可查询React Native的网站。建立React Native是简单的,如果你已经安装了一个最新版的node了,那么就可以使用npm install -g react-native-cli来安装React Native。
一旦你的这些依赖的组件已经安装,运行React Native 初始化项目的名称,将会自动生成一个React Native项目的模板。
一般性的React组件
一旦你的开发环境就绪, 是时候去写一些真正的应用程序。正如之前提到的,React Native真的仅仅是React,但是有几点不同。React Native组件对于浏览器而言,看上去更像是React组件,但是你的基本构建的块已经发生了变化,比如说类似Div、Img或者是p这样的标签。React Native为你提供了基本的组件,比如Text或者是View,在下面的例子中,就用到了基本的组件ScrollView、TouchableHighliht与Text组件,所有的这些,将会映射到Android与iOS指定的View上面去。利用它们创建一个滚动的View,伴随着合适的Touch处理是非常直截了当的:
var React = require('react-native');
var { ScrollView, TouchableHighlight, Text } = R
var TouchDemo = React.createClass({
render: function() {
console.log('pressed')}&
Proper Touch Handling
如果你还没有处理好html与javascript的语法混乱的时候,看上去有些混乱,React强烈建议你使用JSX,对于React Native,你也是别无选择。你的渲染的装饰,是与javascript共同搭配来控制它的行为,这经常会遭到入门者的强烈的反对。但是我强烈建议你给它一次机会。
因为React Native组件同React组件是非常的类似,使得React转换为React Native也是非常的简便。
为了使得绘制更加简单与有效, 同时鼓励便于管理的样式风格, React Native 实现了严格的的子集。那就意味着你没有必要学习指定的平台的方式去设计你的View,但是这需要你花费一点时间去学习如何使用React Native的样式。
最大的不同是你没有必要担心指定的规则,因为样式的继承是相当的精简的,并且React Native使用的内联的样式的语法。
下面是一个样式表在React Native是如何被创建的例子:
var styles = StyleSheet.create({
container: {
marginTop: 30
使用内联语法,这个样式是这样被使用的:
开始移动端的开发
React Native一个更加复杂的地方是开发环境的搭建。 当使用React Native来进行开发的时候,你需要具备所有移动端开发的必备的工具,以及JavaScript的编辑的工具:一个文本编辑器,以及Chrome的调试的工具。
对于iOS,这意味着Xcode的打开,同时包括iOS的模拟器。对于Android,并非是Android Studio,你将会使用命令行工具。最终,你将同时也需要React Native的包运行。你可以选择你的最喜欢的文本编辑器去使用去编辑你的JavaScript的代码。
这样的结果就是你需要有大量的工具在身边。有时候的确是感觉有太多的工具,桌面显的杂乱不堪,太多的桌面窗体打开的确是很烦人。另一方面,至少React Native并没有替你隐藏标准的APP开发的进程。
转向本地代码
React Native通过提供JavaScript接口给已经存在的平台的API,这意味着你可以向一般的React代码那样进行书写,并且React Native &Bridge&将会负责繁重的转换工作,但是如果桥接不完善的时候回发生什么呢?
不可避免,用一个新的框架,比如React Native,将会有一些API会不被支持调用,在这种情况下,可以书写本地代码,在宿主平台与JavaScript代码之间进行衔接沟通。比如:
#import &RCTBridgeModule.h&
@interface MyCustomModule : NSObject
@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&]]);
然后,从JavaScript模块中去使用你的native 模块,包含它就像使用另外的一个Library一样:
var React = require('react-native');
var { NativeModules, Text } = R
var Message = React.createClass({
getInitialState() {
return { text: 'Goodbye World.' };
componentDidMount() {
NativeModules.MyCustomModule.processString(this.state.text, (text) =& {
this.setState({text});
render: function() {
{this.state.text}
你也许使用这个,当一个你需要的API不被支持,如果你希望将已经存在的oc代码或者是Java代码与React Native集成在一起,或者是你希望写一些高操作的函数去处理一些密集的图形处理。值得高兴的是,React Native给一个非常弹性的编写代码的方式,当你需要的时候,使用native module,并且这种方式是直截了当的。即使你之前从未与oc或者是Java打过交道,书写桥接代码是一个非常好的练习,在native移动app的开发过程中获取愉快的感受。Facebook 是如何构建首个跨平台 React Native 应用的?_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Facebook 是如何构建首个跨平台 React Native 应用的?
来源:oschina.net&
作者:coding12, leoxu, 李中凯, gbstack
今年早些时候,我们发布了&。React Native 将开发者在 web 上所使用的 React & 拥有声明式的自包含组件以及快速的开发周期 & 带到了移动平台, 同时保留了原生应用程序的运行速度、保真度及外观。今天,我们很高兴地发布了&。
现在我们已经在 Facebook 的生产环境中使用 React Native 超过一年了。几乎就是一年之前,我们的团队着手开发。当时我们的目标是创建一个新的应用,它能让数以百万计在 Facebook 上做广告的人们能随时随地的管理他们的账户,并创建新的广告。最后的成果不仅仅是 Facebook 第一完全使用 React Native 开发的应用, 而且是首次实现跨平台的一个应用。本文将跟您分享我们是如何构建这个应用的,React Native 是如何是的我们行动更快的,以及我们所积累的教训。
选择 React Native
不久之前,React Native 仍然还是一中没有在生产环境中得到验证的新技术。所以利用该技术开发一个新的应用要承担一些风险,不过这些风险已经被其潜在能带来的好处盖过去了。
首先,我们最初由三个产品工程师组成的团队已经对 React 很熟悉。其次,这个应用需要包含许多复杂的业务逻辑来精细的处理各种不同广告格式、时间轴、日期格式、货币、货币约定等等东西。这些许多都已经用 JavaScript 写好了。全部用 Objective-C 写一遍之后为开发
版本再用 Java 写一遍,这种方案的前景对我们并没有什么吸引力&&当然也并不高效。第三,要实现大多数我们想要构建的 UI 界面&& 以列表、表格或者图像的方式展示许多的数据&&在 React Native 中做会比较容易。了解了 React,产品工程师应该可以快速高效的实现这些视图。
当然,有些特性对这个新平台而言却是是一种挑战 & 例如图像编辑器,它可以用来给广告主缩放和裁剪一张照片,还有地图视图,它可以用来给广告主在以一个位置为中心的特定半径内定位人员。另外一个例子就是面包屑导航,它能帮助广告主看清楚他们账户中的广告层级。这些都向我们提供了将这个平台推向更远的机会。
首先为 iOS 构建广告管理器
我们的团队决定首先实现这个应用的一个 iOS 版本,这样的安排还不多,React Native 一开始也是针对 iOS 而开发的。接下来的几个月我们的团队成员从 3 名工程师发展到了 8 名。新聘人员对 React 不熟悉 & 而他们其中一些人对 JavaScript 也不熟悉 & 但他们都渴望为我们的广告主构建一种优秀的移动体验,所以他们成长得很快。
React Native 团队中有经验的 iOS 工程师帮助我们在 React Native 中桥接了一些终端设备上暂时还用不着的功能特性,比如提供对手机摄像头的访问。他们还帮助我们将已经被应用在其它 Facebook 应用中的,执行用户认证、分析、奔溃报告、网络以及推送通知这些操作的一些 iOS 库,捆绑到了应用。这样就让我们的团队只用去关注构建这个产品。
如上所述,我们能够重用许多之前已经有了的 JavaScript 库。一个这样的库就是&Relay,Facebook 的一个通过&GraphQL&将数据传递到 React 的框架。另外还有一些处理国际化和本地化(这些逻辑在涉及到时间域和货币时会变得有些棘手)的库。通常这些库会从网站的一个 JSON 端点加载正确的配置。我们已经编写了脚本将所有支持的语言导出为 JSON 文件,使用 iOS 的本地化包来引入这些文件,然后拥几行 native 代码就能将 JSON 数据暴露给 JavaScript。这让我们的库几乎不做什么修改就能拿来用。
我们所面对过的比较大一个挑战之一就是导航流。为了对广告主现有的广告和宣传活动进行导航,我们想要有一个导航条。针对广告的创作流程,我们需要一个向导式的导航条。最重要的是,让应用拥有正确的渐变动画以触摸手势也很关键,否则应用感觉上会更像是一个漂亮的移动网站,而不是一个原生应用。
我们的方案就是&&组件, React Native 的 CustomComponents 目录下附带就有这个东西能拿来用。实质上,它就是一个由其他的 React 组件堆积起来的 React 组件集合. 它可以显示这些组件中的一个组件,并且这些组件会在按下按钮或者发生触摸时动画轮换。它还有一个可插入式的导航条组件, 让我们可以实现一个 iOS 风格的,用于大多数一般视图的导航条,用于导航广告和宣传活动的面包屑导航,以及一个用于创建流程的向导式导航。导航条组件的动画进度条可以接收通知,并且匹配性的显示进度增加的动画。这意味着所有的动画,针对视图的以及针对导航条的,都是用 JavaScript 来进行计算的, 而测试显示我们仍然可以以 60 fps 的帧率执行它们。
YouTube 视频地址:&
只有一种情况下导航动画才会有问题,那就是当 Javascript 线程在一个长操作过程中被阻塞时。这种情况基本都是由处理大量新获取的数据引起的。当然当你切换到新的页面,读取和处理新的数据是不可避免的。在一个网速快的环境下, 这个问题可以用正在加载的导航动画来解决。在这里我们采取另一种方案:在动画完成前,是用 InteractionManager 组件显式延迟数据处理(这个是 Reactive Native 内置的组件)。首先我们先切换到包含模板的页面,然后使用 Relay 来做数据处理,接着他会自动调用必要的 React 组件来重新渲染界面。
部署到 Android
既然 iOS 的广告管理器快要部署完成了,我们现在给这个 app 部署一个 Android 版本。使用 Reactive Native 的 Android 移植版应该是最好的选择。幸运的是,Reactive Native 团队已经将 Android 移植版做的够好了。通常我们想尽可能重用代码,不仅是业务逻辑也有 UI,因为大多数页面是基本相同的,为调整样式节省了时间。当然也有一些地方 Android 版的外观和 iOS 版是需要不同的,比如导航,日期选择器和开关按钮等等。
YouTube 视频地址:
幸运的是,React Native 打包器的黑名单功能和 React 的抽象机制帮助我们尽可能的在两个平台之间复用代码,尽可能减少对平台的检查。对于 iOS,可以告诉打包器忽略以 .android.js 结尾的文件。而开发Android 的时候,则是忽略 .ios.js 结尾的文件。这样,我们就可以对同一个组件用 Android 和 iOS 分别实现一次,而使用组件的代码可以是平台无关的。我们不是显示的用 if/else 的方式来检测平台,而是重构平台相关的UI部分,分割成不同的需要 Android 和 iOS 分别实现的组件。在发布 Android 版广告管理器的时候,代码的复用率达到了大约85%。
我们面临的一个大的挑战是如何管理代码。在 Facebook,Android 和 iOS 的代码在不同的代码仓库里。广告管理器的 iOS 版本的代码在 iOS 代码库里,而由于各种原因,Android 版本的代码只能在 Android 代码库里。就像 iOS 版那样,我们需要使用一些 Android 代码库里的 Facebook 的 Android 库。另外,所有的构建、自动化和持续集成工具都绑定了 Android 代码库。如果把已有的 iOS 版代码重构,抽象出平台无关的组件用于 Android 版的移植,则意味着我们要经常 fork 和 merge 相同的代码的两个不同版本。对我们来说,这种情形不能接受。
最后我们决定把 iOS 代码库当做可信的代码源,主要是因为代码已经存在并且 iOS 版的 App 也已经成熟了。我们通过 cron 的任务每天把所有 JavaScript 代码从 iOS 同步几次到 Android 代码库。直接提交 JavaScript 代码到 Android 仓库是不被允许的,除非同时提交到 iOS 仓库。如果同步脚本检测到有不一致的地方,会生成一个任务,后续去进一步检查。
我们同时还做到了让 JavaScript 打包服务器从 iOS 仓库运行 Android 代码。这样的话,那些主要开发 JavaScript,不涉及 native 代码的开发人员就可以基于 iOS 代码库开发和测试自己的代码改动。不过在测试在两个平台上的改动时,还是需要从 Android 代码库构建 Android 应用,从 iOS 代码库构建 iOS 应用,这是一件很麻烦的事情。为了优化 JavaScript 程序员的开发流程,我们开发了一些脚本,用来从持续集成服务器上下载合适的 native 二进制代码文件。这使得大多数的开发者不需要保留 Android 代码库的副本,他们可以直接在可信的 iOS 源代码上开发,这样就可以像在 Facebook 的 web stack 上一样快速迭代。
React Native 团队在这个 App 开发的过程中开发出了 React Native 平台,提供我们需要的组件和接口,使得我们的 App 成为可能。将来这些组件也会给所有的 App 开发者带来便利。即使我们不得不自己实现一些组件,在纯 native 之上使用 React Native技术 也是值得一试的。这些组件是我们不得不实现的,并且可能将来也不会被其他团队重用。
对于我们的教训是,即使有大量的工具和自动化脚本,要跨两个分离的 iOS 和 Android 代码库进行工作是很困难的。在我们开发这个 App 的时候,Facebook 使用的是这种模式,所有的构建自动化工具和开发流程都做了相应的配置。不过,如果一个产品有一个共享的 JavaScript 代码库,这种模式并不合适。幸运的是,&,所有的平台合在一起,只需要一份公共的 JavaScript 代码拷贝,代码同步也不再需要了。
我们遇到的另一个问题跟测试有关。有改动时,所有的工程师都需要小心的在所有的平台上测一遍,整个流程很容易产生人为的错误。然而,使用同一个代码库开发跨平台的应用,这种问题是必然存在的。即便如此,React Native 带来的开发效率以及一开始就在跨平台开发中重用代码带来的优势,远远超出测试不充分导致的偶发问题造成的代价。要知道的是,这个问题不光是产品开发工程师会遇到,React Native 平台开发工程师在开发 Objective-C 和 Java 的时候也会遇到。这些工程师大部分时间都不会只面对一门编程语言。同样,这个问题也会影响JavaScript,例如,开发组件 API 或者部分共享(partially shared)的实现。通常,纯 iOS 开发工程师是不需要测试 Android 上的改动的,对 Android 工程师也是如此。这主要还是一种文化上的差异,需要时间和努力去消除,在这个过程中我们产品的稳定性也在增强。
为了解决这个问题,我们还开发了每次构建都会执行到的集成测试。然而已有的持续集成系统只能在 iOS 平台和 Android 平台分别发现 iOS 和 Android 问题,不能做到在 iOS 的构建上运行 Android 测试,反过来也不行。这需要工程师去解决,不过还是有很多问题,可能会导致app出错。
当这些都搞定之后,我们可以在两个平台上一起发布 Facebook 第一个完全构建在 React Native 上的 app,有着 native 的外观和体验,由同一个 JavaScript 工程师团队完成。当这些工程师加入团队的时候,不是每一个人都熟悉 React Native,然而他们在仅仅5个月的时间就开发出了有着 native 外观和体验的 iOS 版本的 app。在3个月之后,他们发布 app 的 Android 版本。
英文原文:
本文永久更新链接地址:
相关资讯 & & &
& (04/27/:54)
& (09/18/:20)
& (04/27/:18)
& (12/18/:43)
& (09/16/:40)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款}

我要回帖

更多关于 rn tabnavigator 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信