React-Native 之布局UIKit(iOS)
本文探讨一下React-Native(以下简称RN)iOS端的布局过程。
这个过程包括RN从App(模块)启动到最后JSX的View映射成UIKit的View渲染。这里我把这个过程分为两步,第一步加载rootView,第二步rootView加载JSX的view
先看第一步加载RootView:

第一步RootView通常会在程序里这么初始化的。
| 
 | 
 | 
- 这里用RootView做为一个容器,可以理解为webView加载网络资源jsCodeLocation。
- 初始化JS与OC的方法表,这方法表可以理解为他们通信的桥梁。具体原理可以看一下bang的这篇博客。
- 初始化第二部的桥梁,加载OC的本地模块并告诉JS模块
- 同时UI上显示一个Loading 远程资源的状态。
- 等到环境构建完后(远程资源加载到本地内存,JS加载完OC本地的模块)
- 初始话rootView的contentView,在RCTUIManager里设置它作为rootShadowView,关于shadowView后面会再作介绍,这里暂且当它为一个普通的UIView好了。这里面的UI更新频率是根据CADisplayLink的频率来对JS做batchUpdate的。
- RCTRootView通知JS运行JS程序。
- 开始渲染index.ios.js里的入口程序。
- RCTRootView停止LoadingView状态,后续内容的展现全是JS的程序逻辑了。
第二步,UIView怎么通过JSX来实现布局的。
先来埋个伏笔:React-Native是用css3的Flexbox来实现布局的,UIKit要不就是绝对布局(setFrame)要不就是autoLayout,最后UIKit是怎么用的JS里的Flexbox呢?
来看一个简单的例子:
| 
 | 
 | 
这里的语法和HTML很像,只不过Dom节点变成了react似的虚拟Dom节点样式。
看看JS里的View是怎么实现的?
| 
 | 
 | 
这里的createReactNativeComponentClass通过调用堆栈最后是调用到ReactNativeBaseComponent的mountComponent。这里有个很关键的RCTUIManager.createView。
RCTUIManager是什么?它OC本地的一个的桥接模块,这个模块初始化的时候会把所有RCTViewManager的子类都收集起来放到一个_componentDataByName里
JS里面每个特定的基础View模块(比如MapView, WebView)一般都是由OC里的某个特定的ViewManager桥接过来的,如果不是,那也是这些桥接的View的组合(比如ListView)。
重点来了,当JS调用OC的createView:viewName:rootTag:props:后,OC本地会创建一个RCTShadowView。
RCTShadowView看它的基类,它并非一个UIView,而是一个结合layout.c的,这个layout.c文件是Flexbox布局算法的c语言实现。
最后从js传来的Flexbox属性会交给这个shadowView的css_node处理。在前面第一步RootView初始话的第六步里,batchUpdate里,在主线程把所有的shadowViewflexbox模型的计算结果布局到真正的View上面。
大致流程是这样的。
