迁移老文章到掘金(基于RN 0.26)javascript
相关系列文章html
上一篇了解了 ReactNative是如何初始化一整套JS/OC通讯机制,是如何相互通讯的。通篇在讲JS/OC的通讯的源代码流程,解释了为何JS能够调用OC,为何OC能够调用JS,这相互之间的通讯,是如何经过代码进行控制与管理的java
可是上一篇讲的内容有一点太抽象了,全都是底层通讯,咱们依然不知道:node
上层的业务module是如何一步步用js搭建出一款app的?react
因而就进入了今天的环节,ReactNative中的Native,具体讲讲各类各样的Module是如何工做的,官方写好的Module以及咱们能够自行扩展的Moduleios
这里面分为2种modulegit
(说明,官方文档把这个起名就叫源生模块,英文Module,我这里先中二的起名叫APIModule,为了和另外一个区别起名一下,瞎起的名字,你们凑合一下)github
(说明,官方文档把这个起名就叫源生UI组件,英文Component,我这里先中二的起名叫UIModule,为了和另外一个区别起名,瞎起的名字,你们凑合一下)json
API模块阐述了JS是如何调用native各个模块的逻辑react-native
UI组件阐述了JS是如何建立出native的UI界面
本文在源码分析部分,对照前文的代码流程能够加深理解
什么叫APIModule?
APIModule是一种面向过程式的模块调用
JS只须要用一个模块名,一个API名,就能经过bridge找到对应的native的方法进行调用,JS Call OC Method
这个过程就是一个函数调用而已,不存在操做某个实例对象,只是传递参数,操做参数,处理逻辑,返回数值 OC Call JS(经过前文知道,bridge都是异步的,经过callback block返回)
举个例子好了,对于系统alert弹框,分享微信朋友圈,这种功能是最适合使用APIModule的
其实ReactNative原生模块 中文文档上面详细介绍了如何使用APIModule,由于一会咱们还要详细看源码,我这里还会再简单复数一遍。
假如咱们想让RN拥有,iOS系统弹框这一个功能:
第一步,先写一个APIModule对象,听从RCTBridgeModule协议
#import "RCTBridgeModule.h"
@interface VKAlertModule : NSObject<RCTBridgeModule>
@end
复制代码
第二步,在实现文件里写一个宏RCT_EXPORT_MODULE()
第三步,实现这个Module能为RN提供的API。
若是我打算写这样一个函数
-(void)nativeAlert:(NSString *)content withButton:(NSString *)name
让RN去调用,那么按着文档我须要去掉-(void)后的全部内容,写进一个宏里面RCT_EXPORT_METHOD(xxx)
整个代码就会是这样
@implementation VKAlertModule
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(nativeAlert:(NSString *)content withButton:(NSString *)name){
// Use UIAlertView create a alert
}
@end
复制代码
一个最简单的APIModule就写好了,在JS里面想要使用这个APIModule,只须要这样写就OK了
import { NativeModules } from 'react-native';
var VKAlertModule = VKAlertModule;
//而后在须要调用的地方
VKAlertModule.nativeAlert('这是一个系统弹框','肯定')
复制代码
能够看到,在JS中模块名就是咱们建立的类名,function名就是咱们写的OC函数中,第一个参数之前的那一部分(只保留第一个参数前的nativeAlert为名字,后面的withButton什么的都不算了)
咱们以前在源码中提到,JS是能够把回调传回来的,那咱们就改两笔,加入回调的形式
//OC侧代码
@implementation VKAlertModule
RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(nativeAlert:(NSString *)content withButton:(NSString *)name callback:(RCTResponseSenderBlock)callback){
// Use UIAlertView create a alert
// show alert
// 持有 block
// when alert button click ok
// use block callback
}
@end
//JS侧代码
import { NativeModules } from 'react-native';
var VKAlertModule = VKAlertModule;
//而后在须要调用的地方
VKAlertModule.nativeAlert('这是一个系统弹框','肯定',function(){
console.log('clickok');
})
复制代码
此时咱们虽然不知道是怎么回事,只是照着文档作了,但看起来,JS已经彻底能任意的调用APIModule提供的native能力了
使用RN写一个系统alert要作这么多工做么?固然不是,facebook已经帮你写好了一个很是大而全的Alert的APIModule,RCTAlertManager
,因此你彻底能够按着上面的思路去打开RN源码里面的RCTAlertManager类,去看看和学习如何写一个功能强大的APIModule
系统alert这种通用型的需求,facebook帮你写好了,可是若是是分享微信微博朋友圈之类的,facebook固然就不可能把这么独特的中国化的需求提早给你作好,可是不用慌,相信你也能本身写出来同样强大的shareManager-APIModule
知其然知其因此然
咱们只清楚了,如何按着文档写一个APIModule,如何直接让JS去使用module,但为何会这样,这里面代码是怎么运做的,彻底是一头雾水,那么就深刻源码,看看这几个宏是怎么样能产生这样神奇的功效的
RCT_EXPORT_MODULE() 注册APIModule宏
这是一个宏套函数的过程,彻底展开一下能够看到
//宏展开
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }
//宏里面调用的函数
void RCTRegisterModule(Class moduleClass)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTModuleClasses = [NSMutableArray new];
});
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
@"%@ does not conform to the RCTBridgeModule protocol",
moduleClass);
// Register module
[RCTModuleClasses addObject:moduleClass];
}
复制代码
能够看到写了这个宏就自动帮你写好了2个method实现
一个是自动写好了+moduleName
的实现,返回了@#js_name
,@#
的意思是自动把宏的参数js_name转成字符,但咱们刚才的样例里,都是直接不写参数的注册宏,因此说若是注册的时候不写参数,+moduleName
会返回空,此处先不细说,后面会提到
另外一个是自动写了+load
的实现,+load你们都知道,app一运行就会执行一次,全部的类都会执行一次,因此在app运行的时候,你写的这个module类就会自动的执行了RCTRegisterModule
这个函数,这个函数干了些什么事情呢?首先在内存中建立了一个单例RCTModuleClasses表(上一篇中提到过),而后判断你写的类是否听从RCTBridgeModule协议(这也是为何要求你在写module定义的时候必定要组从协议),而后把你写的moduleClass放入内从的单例RCTModuleClasses表中
RCT_EXPORT_METHOD() 导出方法宏
这又是一个宏套宏,看着会有一点晦涩
//最外层宏
#define RCT_EXPORT_METHOD(method) \
RCT_REMAP_METHOD(, method)
//内一层
#define RCT_REMAP_METHOD(js_name, method) \
RCT_EXTERN_REMAP_METHOD(js_name, method) \
- (void)method
//内二层
#define RCT_EXTERN_REMAP_METHOD(js_name, method) \
+ (NSArray<NSString *> *)RCT_CONCAT(__rct_export__, \
RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \
return @[@#js_name, @#method]; \
}
复制代码
能够看一下咱们把-(void)nativeAlert:(NSString *)content withButton:(NSString *)name
这么长一串剪裁掉-(void)
都扔进最外层宏当作参数了,最外层基本上没处理什么,直接调用内一层宏,第一个参数传空,第二个参数透传
看一下内一层宏干了啥,内一层宏除了2个参数透传给内二层宏以外,还从新补全了-(void)
,恢复了一个完整OC语法的函数定义,这样才使得RCT_EXPORT_METHOD(xxx)这样写一个函数编译器不会报错
最重要的内二层咱们看看都作了啥,RCT_CONCAT又是一个宏,这个宏我就不展开了,他基本上就是实现了一个宏的拼接,最早把__LINE__
与__COUNTER__
进行拼接,这是两个C语言宏,分别表明着行号与一个内置计数器,我没有详细去跟这两个数字的具体表现,大概意思就是为每个RCT_EXPORT_METHOD生成一个惟一识别的数字tag在将这个tag与js_name拼接(此处其实js_name为空字符串),而后在前面拼接上一个__rct_export__
,用宏生成了一个返回NSArray的方法
说这有点绕举个例子就行了,假设咱们写RCT_EXPORT_METHOD(nativeAlert:xxx)
的时候,__LINE__
与__COUNTER__
组合起来的数字tag若是是123456,那么这个内二层宏还会自动生成一个这样的函数
+ (NSArray<NSString *> *)__rct_export__123456{
return @[@"", @"nativeAlert:xxx"];
}
复制代码
换句话说,一行RCT_EXPORT_METHOD(xxxx),等于生成了2个函数的实现。
-(void)nativeAlert:(NSString *)content withButton:(NSString *)name
+(NSArray<NSString *> *)__rct_export__123456
咱们native注册的这些modules表,导出的这些自动生成的方法,JS是怎么知道的?怎么调用的?
这就紧密联系前一篇文章提到的RCTRootView的初始化环节中的几个重要标记了
InitModule的时候,就会从单例RCTModuleClasses表中拿出全部的APIModule的class对象,循环去建立RCTModuleData实例(上文提到过RCTModuleData不是APIModule,而是包装了一下APIModule,RCTModuleData.instance才是APIModule),而且一一保存在RCTBatchBridge对象的三个表中
+methodName
方法的返回值为key,枚举由APIModule生成的RCTModuleData对象,添加进入字典 (刚才不是说注册宏咱们历来都不填参数,致使+methodName返回为空字符串么,这里经过RCTBridgeModuleNameForClass方法,若是是空字符串会自动返回类名字符串)moduleConfig的时候,RCTBatchBridge会循环moduleDataByID数组表,把每个APIModule的name都写进数组,而后写进key为remoteModuleConfig的字典,最后序列化成JS,造成相似这样的json,全部的RCT开头的都是facebook官方写好的APIModule
{"remoteModuleConfig":[["VKAlertModule"],
["RCTFileRequestHandler"],
["RCTDataRequestHandler"],
...]}
复制代码
moduleConfigInject的时候,会经过RCTJSExecutor,把这个json注入JSContext,在JS的global全局变量里面加入一个__fbBatchedBridgeConfig
对象,是一个数组,里面记录着全部APIModule的name,这样至关于告知了JS,OC这边有多少个APIModule分别都叫作什么,能够被JS调用,但此时尚未告诉JS,每个APIModule,均可以使用哪些方法
上一篇还提到了一个JS Call OC的方案,[RCTJSExecutor setUp]
中设置了一大堆JSContext[“xxx”]=block
的方法,这里面有一个名为nativeRequireModuleConfig
的JSContext的block注入
当evaluateJS标记的时候,JS就会主动callnativeRequireModuleConfig
这个方法,从而调用了这个blck,从名字能够猜出来,前面咱们把全部的APIModule的名字列表发给了JS,这下JS开始用名字,找OC一一确认每个APIModule里面都有啥具体信息,具体Method方法。
经过名字,block会找到对应的RCTModuleData,从而调用RCTModuleData-Config方法
__rct_export__
开头的方法(上文提到过)
nativeAlert:(NSString *)content xxxx
nativeAlert
做为JS简写方法名["VKAlertModule",["nativeAlert"]]
这样JS就彻底知晓,Native全部APIModule的名字,每一个APIModule下全部的Method的名字了
JS Call NA的时候
除了主动的block式callNA之外,前一篇文章提到了nativeFlushQueueImmediate
这个JS主动call OC的方法,经过nativeFlushQueueImmediate的逻辑能够看出,当JS每次想主动调用OC的时候,会把全部JS消息都扔到JS的messagequeue的队列里,而后每一个5毫秒,会触发nativeFlushQueueImmediate
的block,让OC主动去发起一次flushedQueue
来把这段时间内全部的JS消息都拉去过来,这就走到了前一篇文章提到过的
- (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded
再日后就是上文介绍过的 handleBuffer分发逻辑
- RCTBatchedBridge-handlebuffer - analyze Buffer(analyze buffer标记) - find module(find modules标记) - for 循环all calls - dispatch async(dispatch async标记) - [RCTBatchedBridge- handleRequestNumber:] - [RCTBridgeMethod invokeWithBridge:](invocation标记 这个标记会复杂点,子流程表细说)
基本上就是,找到对应的RCTModuleData,找到对应的APIModule,找到对应的RCTModuleMethod,执行invocation,完成了调用
以上就是一整个JS Call Native APIModule的源码流程交互图,那种API型的功能,好比系统弹框,好比社交分享,都是经过这样的运做流程,才能让React在JS环境中自由的调用native源生API模块
这里提一个遇到的坑
当咱们写RCT_EXPORT_METHOD()宏的时候,写导出给JS的函数的时候,若是参数中含有successcallback,errorcallback,切记把这种callback block放在最后,千万不要,把其余类型的参数放在block以后。
缘由是在JS代码一侧有last arg,second arg的判断,当callbackblock 不是以倒数第二第一的位置出现的时候,JS会报exception
//正确的作法
RCT_EXPORT_METHOD(nativeAlert:(NSString *)content withButton:(NSString *)name callback:(RCTResponseSenderBlock)callback))
//错误的作法
RCT_EXPORT_METHOD(nativeAlert:(NSString *)content withCallback:(RCTResponseSenderBlock)callback) withButton:(NSString *)name)
复制代码
能让React在JS环境中自由的调用native源生API模块,只是实现一个app很小的一部分,若是能React在JS环境中自由的建立Native源生的UI界面,自由的修改,变化每个UI界面的展示效果,才是实现一个app最重要的一环,因而咱们进入了下一部分 源生UI组件模块
什么叫UIModule?
UIModule是一种面向对象式的UI组件调用
每个React的Component都是一个独立的UI组件,通过React的flexbox排版计算,有本身的大小,形状,样式。
每个被RN渲染出来的RCTView都是继承自UIView的纯源生UI组件,他是根据React的Component的计算结果(大小,形状,样式)从而建立出来的。
RCTView与Component是一一对应的
当一个JS Component对象想要建立/改变本身的颜色,大小,样式的时候,就须要经过brdige找到本身所对应的那个RCTView,传递过去相应的数据参数,对RCTView生效传来的数据 JS CALL OC
当RCTView发生了触摸等源生事件响应的时候,经过brdige找到本身所对应的JS Component,把触摸的事件和数据参数传过去,让React.JS根据数据进行JS Component的从新布局或者界面响应
UIModule 其实由2部分组成,RCTView与RCTViewManager,就好像v与c的关系同样,每一个UIModule都会有一个RCTComponentData与之配合(就好像APIModule与RCTModuleData同样)
正式由于每个JS的Component都是与一个UIModule创建了一一对应的关系,因此当发生渲染的时候,JS Component的渲染信息,就会经过brdige,生成继承自纯源生iOS UIKit的UIModule
每个JS的Component与之直接配合的都是RCTViewManager,这是一个继承自NSObject听从RCTBridgeModule协议的类,若是你打算本身写一个自定义的UIModule,也是须要继承自RCTViewManager,他只是一个控制器的角色,所以RCTViewManager还须要决定他采用什么方案进行绘制,因此在RCTViewManager能够选择使用不一样的UIView来实现真正的视图角色。
老规矩,ReactNative原生UI组件 中文文档上面详细介绍了如何使用UIModule,由于一会咱们还要详细看源码,我这里还会再简单复数一遍。因为UIModule要处理的东西,官方文档源码都比较详细了,因此我不会太细致的介绍。
自定义一个UIModule组件
-(UIView *)view
指定视图真正实现上面全都是按着文档的流程去操做,你就能够在JS中以React.JS的方式去构建一个纯源生native组件了
...
render: function() {
return (
<View style={this.props.style}> <XXUIModule ... /> </View> ); } }); 复制代码
知其然知其因此然
咱们只清楚了,如何按着文档写一个UIModule,如何直接让JS去使用这个UIModule对应的JS Component,但为何会这样,这里面代码是怎么运做的,依然是一头雾水,那么就深刻源码,看看这几个宏,这几个native oc方法是怎么运做的
RCT_EXPORT_MODULE() 注册Module宏
跟APIModule注册是同一个宏,都是在一个单例RCTModuleClasses表中把xxRCTViewManager添加进去,不作多解释
-(UIView *)view
方法
RCTComponentData-createViewWithTag
这个方法会调用RCTViewManager的view方法,前边讲过RCTComponentData的角色
而RCTComponentData-createViewWithTag
这个方法会被一个RCTUIManager
调用,当真正须要渲染的时候,RCTUIManager会经过这个方式决定,到底应该alloc,init出一个什么样的UIView(RCTView?MKMapView?UIImageView?)
这里提到了一个关键词RCTUIManager
,先按下不表,后面咱们会详细说明
requireNativeComponent为React.JS导入源生component
这块就得看JS的源码了,就是下面这个文件
node_modules/react-native/Libiraries/ReactIOS/requireNativeComponent.js
我对JS没那么深的了解,大体看了下这里一直在操做一个叫作UIManager
,从UIManager按名字取出ViewConfig的配置,而后配置了一大堆内容,那咱们就打开这个文件
node_modules/react-native/Libiraries/Utilities/UIManager.js
看到了这样一行代码var UIManager = require('NativeModules').UIManager;
眼熟么?没错,这就是APIModule在JS文件中使用的时候,须要的require,换句话说,这个UIManager操做的就是RCTUIManager
,RCTUIManager先按下不表,后面咱们会详细说明
OC属性导出宏
//常规导出宏
#define RCT_EXPORT_VIEW_PROPERTY(name, type) \
+ (NSArray<NSString *> *)propConfig_##name { return @[@#type]; }
//自定义导出内置宏
#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \
+ (NSArray<NSString *> *)propConfig_##name { return @[@#type, @#keyPath]; }
//自定义导出宏
#define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \
RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \
- (void)set_##name:(id)json forView:(viewClass *)view withDefaultView:(viewClass *)defaultView
复制代码
看常规导出宏,##在宏里面的用法就是字符串拼接,@#在宏里面的用法就是参数转字符,换句话说RCT_EXPORT_VIEW_PROPERTY(isHidden, BOOL)
的做用就是生成了一个方法
+ (NSArray<NSString *> *)propConfig_isHidden {
return @[@"BOOL"];
}
复制代码
propConfig_isHidden这个函数被谁调用了呢?RCTComponentData的setProps:forView:
方法,这个方法被谁调用了呢?RCTUIManger,嗯,一会细说
看自定义导出宏,这个宏被用来导出一些很是规类型的属性,一些自定义的结构体,对象类型的属性,他首先调用了自定义导出内置宏,着红看起来和刚才的宏差很少,只不过返回的字符串数组多了一个值,他还又单首创建了一个新函数,举例说明,若是咱们写了一行RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
(官方文档的例子),就至关于自动添加了2个方法,第一个方法已经在宏里实现了,第二个方法写完宏后自动生成了声明,但实现须要使用者跟着立刻补上(如同RCT_EXPORT_METHOD)
+ (NSArray<NSString *> *)propConfig_region {
return @[@"MKCoordinateRegion",@"__custom__"];
}
- (void)set_region:(id)json forView:(RCTMap *)view withDefaultView:(RCTMap *)defaultView
复制代码
第一个方法方法和常规属性导出宏做用同样,都会被RCTComponentData,RCTUIManger调用,详细内容后续说明
第二个方法在哪调用呢?调用的位置牢牢挨着第一个方法执行,在第一个方法propConfig_xx执行事后,会判断是否还有@"custom"标记,若是含有就会调用第二个方法,详细内容仍是属于RCTComponentData,RCTUIManger,后续会说明
至于JS Component封装,属性propTypes声明,这就属于React.JS的特性了,反正最后仍是经过JS的UIManager去操做Native
OC用属性导出宏建立事件
一个UIView必须具有一个RCTBubblingEventBlock型的block属性,才能够被当作事件导出
这个block属性导出和常规属性导出,都是同一个宏RCT_EXPORT_VIEW_PROPERTY
只不过type必须是RCTBubblingEventBlock,宏的工做流程是一致的,区别只是RCTComponentData,RCTUIManger在处理上的不一样而已,后续说明
建立常量
这个constantsToExport方法会返回一个字典,在RCTModuleData的gatherConstants函数中被调用,而这个函数会被RCTModuleData的config方法调用
这个在APIModule的时候提到过,在injectModuleConfig的时候,获取config,转成json,最后会注入js,成为js能够获取到得常量
终于来讲RCTUIManager
与RCTComponentData
,上面提到了无数次,这两个东西加上UIManager.js
构成了整个RN能够经过js建立native界面的核心
首先强调一点,RCTUIManager是一个APIModule,RCTUIManager是一个APIModule,RCTUIManager是一个APIModule,重要的事情说三遍
这个东西就有意思了,这是一个APIModule,所以就像其余全部APIModule同样,他会被RCTModuleData管理着,最重被RCTBatchBrdige持有着,时刻等待着JSUIManager.js
的调用
可是他就像RCTBridge同样内部也维护了不止一个字典,管理着全部的UIModule,以及全部的View,他在初始化的时候RCTUIManager-setBridge
_componentDataByName
这个内部字典表,以ModuleName为Key让咱们看看,当ReactNative开始建立界面的时候,都会发生什么事情?当进过上一篇提到的__evaluateJS标记__以后,并不会马上开始绘制RN界面,为啥?输入了JSBundle之后,整个JS环境就已经彻底配置完毕,ready就位了,可是并不会真正开始绘制界面,绘制界面会经过开发者,自行建立RCTRootView,而且执行initWithBridge后开始(这里我并无说initWithBundleURL,两者流程是如出一辙的,可是initWithBundleURL与initWithBridge的区别我在下一环节会特别说明)
由于不是很长,我就不安着这标记那标记的解释了,顺着说一下。
首先建立RCTRootView的时候若是bridge已经搭建完毕,JS环境已经就位,那么就会直接出发bundleFinishedLoading
,若是JS环境没有就位,那么就会等待JS环境运行完毕Ready后,经过通知触发bundleFinishedLoading
在开始正式建立RCTRootView的时候会建立一个subviewRCTContentRootView
这个东西建立的时候须要一个reactTag,这个tag是一个很关键的东西,此时经过allocateRootTag
方法建立了root得reactTag,规则是从1开始,每次建立一个RootView实例都会累加10,1,11,21,31,以此类推。建立完RCTContentRootView后还要去UIManager用这个reactTag注册View,也就是以Tag为Key,登记进入_viewRegistry字典表
而后将RCTContentRootView添加到RCTRootView上面,执行了runApplication,这里面真正的意义是执行了一行JS代码,告诉JS你要开始绘制这个参数params的界面了!
AppRegistry.runApplication(params)
再日后就是React.JS的工做了,React.JS会着手把JS中的页面进行计算,排版,生成对应的JS Component,准备组织绘制界面了,包含着无数个JS Component的相互嵌套。最重经过UIManager.js
这个APIModule的JS接口,开始call oc去建立界面
__RCTUIManager__都有哪些API提供给了JS呢?
咱们能够想象一下,当React.JS开始工做的时候,JS把全部布局好的Component,一层套一层的JS界面数据,经过UIManager,调用createView,updateView,setChildren等接口API,来建立一个个纯iOS native的UIKit的界面。
有兴趣的话,彻底能够在runApplication
,createView
,updateView
,setChildren
等处打上断点,看看是否是像我说的同样。
这样一来,基本上就完成了React.JS建立一个纯native界面的过程,但咱们仍是具体以createView举例,深刻分析一下这里面的源码,这里面有不少咱们在前文提到的未解之谜(各类RCTComponentData去调用ViewManager的过程)
propConfig
拼接props的名字来确认,此property是否已经使用EXPORT宏注册过属性(上面提到过)看到这个过程没有,整个过程有一个最核心的点ReactTag,为何说这个tag核心呢?由于咱们都知道APIModule的特色就是面向过程的,他是不存在对象self这个概念的,因此必须经过这个tag,在JS里面有一个全部JSComponent的tag表,在OC里面依然也有这么一个全部nativeView的Tag表_viewRegistry
,只有经过惟一指定的tag,这样APIModule-RCTUIManager,才能知道到底应该操做哪个nativeView
咱们顺带来看一看JS侧的代码吧~看看JS那边是怎么操做和读取这个tag的吧~比较简便的方式是,查JS源码代码,看看各处都是如何调用UIManager.js
的,我对JS没那么熟,不细说了,怕说错,你们能够本身看看。
这里要介绍一下JS那边tag是如何管理的,有个ReactNativeTagHandles.js
的JS模块,require了ReactNativeTagHandles之后在Chrome内存中能够看看ReactNativeTagHandles这个对象都有什么内容,你会发现,它内部存着一大堆的表格
你会发现他里面存着这样的东西
rootNodeIDToTag:Object
.r[1]{TOP_LEVEL}:1
.r[1]{TOP_LEVEL}[0]:2
.r[1]{TOP_LEVEL}[0].1:27
.r[1]{TOP_LEVEL}[0].$1:3
.r[1]{TOP_LEVEL}[0].$1.0:4
.r[1]{TOP_LEVEL}[0].$1.0.0:5
...
复制代码
这里面不只保存着tag信息,还保存着至关多的层级信息,还有其余信息,简单的发现最后一个数字大概表明着reactTag,r[n]中的那个n表明着,component所在的rootView的tag
由于这样的内容确定不方便读写操做,因此ReactNativeTagHandles还提供了不少方法能够用
我JS不是很熟悉,这部分就细细解读了
这里就简单的说一下,咱们由于业务缘由,注定不可能以单一RCTRootView去实现整个APP功能,注定了大部分保留现有native功能,个别动态性较强的新功能采用ReactNative去开发
因此咱们采用的是多RCTRootView得方式,什么意思呢,建立一个RNViewController类,这个类内部有一个RCTRootView当作界面,可是整个RNViewController被当作其余natve的UIViewControler同样,去push,去present,去pop,并无使用ReactNative里面的navigator组件
既然选择了这种模式就要注意一个问题,facebook其实也在源码的注释中强调,若是你还有多个RCTRootView,推荐让全部的RCTRootView共享同一个RCTBridge,毕竟上一篇文章咱们就讲了,整个RCTBridge的初始化流程仍是至关的复杂,挺耗性能的,既然是一个JS环境,干脆全部的rootview公用同一个JS环境,也就是JSBridge
这就引起了我前面提到过的,RCTRootView建立的时候,跟常规开发文档demo都不一样的方式initWithBridge,首先我选择在app启动的时候,就建立初始化整个JS环境,JSBridge,(上一篇分析过,这里面有不少异步处理,不用担忧卡主线程UI),等到用户要点击弹出RN页面的时候,再去构建RCTRootView,使用initWithBridge的方式。
常规的开发文档demo都选择initWithBundleURL的方式,这个方法其实就是,把initJSBridge,与initRootView打包处理了,很适合整个app都是reactnative的开发模式,可是咱们就不适用了
从自定义的APIModule中JS call OC,OC如何知道这个JS Call来自哪一个RootView
我就碰到了这样的一个坑,发现坑了后,读了RCTUIManager的源码才按着UIManager的思路解决了问题,没错就是reactTag,当你但愿JS经过APIModule调用na的时候,在JS调用前先找到本身component所在的rootViewTag,把这个tag随着API的参数一块儿发过来,而后直接经过RCTBridge.uimanager的方法获取RCTUIManager,从而查找整个_viewRegistry[tag]
表,最终快速定位到,JSCall来自哪一个RootView