React Native 分析(六)如何整合进现有 app
RN 的机制就是所有代码,包括底层代码和业务代码,默认都打成一个 bundle 包
这个 bundle 包就会面临着加载速度的问题。
下载速度也是问题,没压缩500多 k,压缩100多,还是不包含业务代码的情况下。
命令行打包工具支持 unbundle,但是 iOS 还是一个大 bundle,因为 iOS 加载多个小文件速率有问题。
优化有两个方向
一、拆分 js 模块,拆分出通用底层模块和业务模块,底层模块预先加载。
问题:
如果每个业务创建一个 js 引擎,那么就会创建多个 js 引擎,那么就会面临着内存占用多的问题。
二、复用 js 引擎
js 执行引擎只是一种通信机制,因此其实一个就够了,可以想办法复用起来。
第一步就是把 init rootview 和 init bridge 拆分,也就是把引擎和界面拆分。
输入了JSBundle以后,整个JS环境就已经完全配置完毕,ready就位了,但是并不会真正开始绘制界面。
我们的 app,现有状况决定了不可能以单一RCTRootView去实现整个APP功能,注定了大部分保留现有native和 h5混搭功能,个别动态性较强的新功能又追求性能的情况下可以采用ReactNative去开发
所以打算采用的是多RCTRootView得方式,创建一个RNViewController类,这个类内部有一个RCTRootView当做界面,但是整个RNViewController被当做其他natve的UIViewControler一样,去push,去present,去pop
可以让所有的RCTRootView共享同一个RCTBridge,整个RCTBridge的初始化流程还是相当的复杂,挺耗性能的,既然是一个JS环境,干脆所有的RootView用同一个RCTBridge
在app启动的时候,就创建初始化整个JS环境RCTBridge,等到用户要点击弹出RN页面的时候,再去构建RCTRootView,使用initWithBridge的方式。
创建RCTRootView的时候如果bridge已经搭建完毕,JS环境已经就位,那么就会直接出发bundleFinishedLoading,如果JS环境没有就位,那么就会等待JS环境运行完毕Ready后,通过通知触发bundleFinishedLoading。
- (void)bundleFinishedLoading:(RCTBridge *)bridge
{
...
[_contentView removeFromSuperview];
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
bridge:bridge
reactTag:self.reactTag
sizeFlexiblity:_sizeFlexibility];
[self runApplication:bridge];
_contentView.backgroundColor = self.backgroundColor;
_contentView.passThroughTouches = _passThroughTouches;
[self insertSubview:_contentView atIndex:0];
...
}
本质上 Bridge只是通信机制,那么如何标明这个请求来自哪个 rootview 呢?通信机制只包含了调用方法的必要信息,缺乏上下文信息。
一般来说,一个 rootview 代表了一个模块,缺乏上下文信息也能正常使用。但一些情况下,就是识别这个请求发自哪个 rootview 了。
可以在JS调用前,可以通过findNodeHandle(this)._hostContainerInfo._tag 找到component所在的rootViewTag,把这个tag随着API的参数一起发过来,然后直接通过RCTBridge.uimanager的方法获取RCTUIManager,从而查找整个_viewRegistry[tag]表,识别JSCall来自哪个RootView。
关于集成效果,可以去应用市场搜索我们开发的应用“乐车邦”来体验RN 的感觉。
好了,这系列文章算是告一个段落了,各位看官过瘾了没?