React Native 初探

不知从何时起,移动端App开发,采用Native还是使用Web的争论不绝于耳。二者的优缺点不再赘述。Web App当然是开发者期待的理想结果,但是由于Native App在用户体验上的绝对碾压,大部分移动端App还是采用Native的方式,少数架构复杂、对Web依赖较多的App,会采用一种称为Hybrid(Web + Native)的开发方式,在iOS上,Native通过-[UIWebView stringByEvaluatingJavaScriptFromString:]调用Web,而Web则是通过设置WebView iframe的src,搭建JSBridge进行通讯。由于微信和手机支付宝的的成功,Hybrid App这种开发方式确实引起了关注,但从我这么一个最底层iOS开发者的技术角度来说,这种JSBridge的通讯方式,实在不是特别高明,能解决的场景也十分有限。

前几天FB正式推出了React Native。由于惯性思维,我总想着往它身上贴个「Web」或者「Native」或者「Hybrid」的标签,可是贴上去扯下来,并没有一个适合的标签。事实上,React Native重新定义了一种新的模式。

浏览器引擎是如何工作的

在说React Native之前,让我们以WebKit为例,先扯一扯一个浏览器引擎的工作流程。从下图可以看出,一个网页的生命周期,大致经历了加载、解析、排版、绘制(JS引擎暂时不提)。

接触过iOS平台上的简易的浏览器引擎,大致的工作流程,也是如此。由于加载流程涉及网络模块,部分排版和渲染流程涉及Native UI控件,为解决不同平台的差异性,一般是抽象接口,由不同平台实现各自的网络模块和网页的绘制。

简单来说,一个浏览器渲染引擎,其实就是将网页从服务器或者本地load下来,用一套规则解释这个网页,最后用平台最舒服的方式,展现到屏幕上去。

React Native

一个浏览器引擎

由于对浏览器印象深刻,这是React Native给我的第一印象。由于我对前端的了解,只停留在html和Javascript的简单语法上,完全不知ReactJS为何物,所以我只能尝试着从开源的iOS React Native的OC端代码,解释一下。

加载:OC层加载JS源数据(可以称为:使用ReactJS框架的?),并利用JavascriptCore.framework搭建起OCBridge,作为和JS层通讯的工具。 解析:解析过程由JS端完成,通过JSBridge,调用OC层将解析结果映射到Native(事实上并没有JSBridge,后面细讲)。映射结果包括了视图的层次结构,Native UI节点的属性值(颜色、文字内容等)。 排版:OC层通过css-layout确定节点的位置。 绘制:Native UI节点进行drawRect。 得益于JavascriptCore,React Native能够抛弃WebView直接运行JS,在React Native,OC层只负责控制程序生命周期,以及提供平台Native控件的工作;而JS层则负责提供数据,响应交互事件,充当了DataSource和Delegate的角色。

通信机制

这里的通信,是指JS和OC之间的通信。

前面已经提到,OCBridge是利用JavascriptCore直接调用JS代码的。OC层实现这个类的是RCTBridge,目前的代码是使用RCTContextExecutor作为具体的执行者。JavascriptCore是iOS7才开放的接口,不过目前的代码还有另外一套RCTWebViewExecutor,里面用的是通过UIWebView调用JS,可能是为了以后兼容旧版本的iOS。使用JavascriptCore最显而易见的优势就是,整个执行过程都可以在后台线程执行,事实上RCTContextExecutor单独开了一个名为「com.facebook.React.JavaScript」的线程,供自己使用。

上面只提到OCBridge,那JSBridge呢?

答案是,没有JSBridge。前面提到,OC层提供Native控件,JS层更多地是扮演DataSource和Delegate的角色。回想一下UITableview的使用,为UITableview设置DataSource和Delegate之后,使用者并不需要关心UITableview是如何被创建绘制,以及如何监听点击长按之类的交互事件。同理,JS层作为使用者,并不需要关心Native事件是如何触发的,需要关心的是,当事件触发时该如何响应。所以,一个原本需要双向通信的机制,被简化成单向通信。

这个机制,可以通过查看 -[RCTBridge enqueueJSCall:args:]这个函数的Callers来验证(这个函数是OC层调用JS的入口函数),它的 Callers包括了:Device Event(如前后台切换)、Input State(如控件Value改变)、Timer回调、Touch事件回调等等。

那JS层是如何实现调用OC层的呢?是通过返回值。在事件触发OC层调用JS之后,会获得一段JSON数据作为返回值,OC层只需要按照协议,解析这段JSON数据,依次调用Native代码即可。

通信协议

JS调用OC的协议,是-[RCTBridge setUp]的时候,通过 RCTRemoteModulesConfig()创建并传给JS层的。 RCTRemoteModulesConfig()主要做了几个事情:

通过 RCTBridgeModuleClassesByModuleID() 遍历所有的OC类,取出所有符合 RCTBridgeModule protocol的module,以moduleID做标识。 遍历第一步取到的类,通过RCTExportedMethodsByModuleID()取出每一个类暴露给JS层的OC method,以methodID做标识,打包到module中 第二步中,暴露给JS的method,接口实现的第一句都会加上RCT_EXPORT(js_name)这个宏(实现机制十分奇特,这里不提)。 假如module需要传递给JS一些常量(比方说Native UI控件的属性枚举值),则通过实现-[RCTBridgeModule constantsToExport],打包到module中。 将所有的module打包成Config Dictionary 当JS返回JSON数据时,实际上返回了一段包含了moduleID和methodID的队列,OC层按照协议的约定,执行对应方法。

至于OC调用JS的协议,也是通过module、method来标识的。不过这些module、method都是OC层写死的字符串,应该是和JS强绑定的,没有啥特殊之处。

解析和排版

浏览器引擎,离不开的就是dom tree 和render tree。简单来说,dom tree 是根据源数据解析而来的,包含了原始的节点信息;而render tree 则是dom tree + css。排版的目的,就是生成render tree,确定每个节点在屏幕上的大小位置。

在React Native中,解析过程是在JS层完成的,原理未知。在OC层,RCTUIManager负责将JS层的解析结果,映射到OC层的视图层级,它本身不做任何的解析操作,只是提供方法,让JS层调用而已。最终dom tree映射到OC层的结果,是一棵�「RCTShadowView tree」。RCTShadowView这个名字也起得很有意思,它不是真正展现的视图,只是一个映射结果而已,每一个RCTShadowView对应一个真正的视图。RCTShadowView的另一个意义在于,它拥有一个成员变量cssNode,可以通过FB的开源项目css-layout(代码里面难得一见的两个C文件),完成排版。剩下的细节工作,就交给RCTShadowView对应的真实视图了。

其实一开始并没有打算看源码的,只是因为Demo中一张图片无法显示,让我不得不调试图片下载模块来确定问题 -_-|||(图片下载使用的是NSURLSession,这货也是iOS7才有的接口,看来React Native还没打算支持旧版本的iOS)。时间匆忙,水平有限,肯定错误连篇,还望指正。 http://www.hotobear.com/?p=1015

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Coding迪斯尼

Reactjs+BootStrap开发自制编程语言Monkey的编译器:创建简易的页面IDE

1682
来自专栏恰同学骚年

微信小程序开发初探

  (1)一切以用户价值为依归→用户是微信的核心,所以微信中没有很多与客户无关的功能,比如QQ中的乱七八糟一系列东西。

3533
来自专栏老马寒门IT

11-移动端开发教程-zepto.js入门教程

Zepto.js是一个轻量级的针对现代浏览器的JavaScript库, 它与jquery有着类似的api。 如果你会用jquery,那么你也会用zepto。 1...

3475
来自专栏司想君

React编程思想

在我们团队看来,React是使用JavaScript构建大型、快速的Web apps的首选方式。它已经在Facebook和Instagram项目中,表现出了非常...

4109
来自专栏PHP在线

最新HTML5学习路线整合

HTML5是万维网的核心语言,标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次重大修改,一方面提升了用户体验,另一方面HTML5技术跨平台,适配多...

1854
来自专栏司想君

React编程思想

能够按照构建的方式来思考web app的实现,是React众多优点之一。在这篇文章中,我们将引导你进行使用React构建可搜索产品数据表的思考过程。

5165
来自专栏Python数据科学

给爬虫爱好者的福利 SelectGadget

相信爬虫的爱好者们都经历过这样一个爬虫前期的准备过程,那就是用浏览器的审查元素进行爬取目标的定位。每次我们都要浪费部分时间去寻找定位点和xpath,这样既不很方...

941
来自专栏杨龙飞前端

react的一些思考

在做好第一个需求之后,我接到了一个react写的产品,这让我异常的兴奋,终于能写react了

853
来自专栏更流畅、简洁的软件开发方式

自然框架,拆分后的项目关系

  拆分了一下自然框架,似乎又绕回去了。以前是多个项目分开放的,有人说太分散了,还得一个个下载,麻烦。于是就做了一个解决方案,把项目都放在了一起。   现在呢,...

2265
来自专栏企鹅号快讯

史上最全的web前端学习教程汇总!

第一阶段:HTML+CSS HTML进阶、CSS进阶、div+css布局、HTML+css整站开发、 JavaScript基础:js基础教程、js内置对象常用方...

4105

扫码关注云+社区

领取腾讯云代金券