RCTRootView 创建前到中
获取URL
从localhost/ip获取,或者从本地获取1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38[[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"
fallbackResource:nil] = {
return [self jsBundleURLForBundleRoot:bundleRoot
fallbackResource:resourceName
fallbackExtension:nil] = {
NSString *packagerServerHost = [self packagerServerHost] = {
// debug模式下如果没有内置ip.txt,则返回localhost,release则返回nil
NSString *location = [self jsLocation];
if (location != nil) {
return location;
}
NSString *host = [self guessPackagerHost] = {
// ...
NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"ip"
ofType:@"txt"];
// ...
NSString *host = ipGuess ?: @"localhost";
// ...
};
if (host) {
return host;
}
return nil;
};
if (!packagerServerHost) {
return [self jsBundleURLForFallbackResource:resourceName
fallbackExtension:extension];
} else {
// http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false
return [RCTBundleURLProvider jsBundleURLForBundleRoot:bundleRoot
packagerHost:packagerServerHost
enableDev:[self enableDev]
enableMinification:[self enableMinification]];
};
};
创建 Bridge
1 | [[RCTRootView alloc] initWithBundleURL:jsCodeLocation |
RCTBridge 创建
1 | RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL |
RCTBridge setUp
RCTCxxBridge 创建
1 | - (instancetype)initWithParentBridge:(RCTBridge *)bridge { |
RCTCxxBridge start
1 | - (void)start { |
RCTModuleClasses
每一个导出类都会实现RCTBridgeModule
的代理方法
1 | @protocol RCTBridgeModule <NSObject> |
RCT_EXPORT_MODULE里实现了moduleName方法,同时也在类的load
方法里注册本类,下面来看下RCTRegisterModule
里的方法
1 | void RCTRegisterModule(Class moduleClass) { |
RCTModuleData 创建
1 | - (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<id<RCTBridgeModule>> *)modules |
executorFactory.reset()
初始化JS执行环境,以及初始化devSettings
模块以便调试用
RCTModuleData 创建Module时,通过runtime查找出所有方法名前缀是 __rct_export__
的方法生成RCTBridgeMethod对象,当需要调用本地方法时,调用processMethodSignature
,先生成方法的签名(NSMethodSignature),再生成 NSInvocation
, 设置参数,调用 invoke
方法,完成调用。
下面是JS需要原生回调数据时的情况:
_initializeBridge
1 | - (void)_initializeBridge:(std::shared_ptr<JSExecutorFactory>)executorFactory { |
在JSCExecutor的构造函数中调用initOnJSVMThread()
和installGlobalProxy()
两个方法
1 | void JSCExecutor::initOnJSVMThread() throw(JSException) { |
1 | // 绑定nativeModuleProxy为NativeModules |
loadSource
loadBundleAtURL
1 | + (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete { |
这里以远程数据为例,调用attemptAsynchronousLoadOfBundleAtURL
方法
1 | static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, RCTSourceLoadBlock onComplete) { |
下载完JS数据后,开始执行
1 | - (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync { |
JSCExecutor::loadApplicationScript
1 | void JSCExecutor::loadApplicationScript( |
bindBridge
1 |
|
映射了JS中的callFunctionReturnFlushedQueue、invokeCallbackAndReturnFlushedQueue、flushedQueue和callFunctionReturnResultAndFlushedQueue四个方法
m_flushedQueueJS->callAsFunction({})
从JS中取出待处理的队列
callNativeModules()
映射成调用本地方法
RCTRootView 创建
1 | - (instancetype)initWithBridge:(RCTBridge *)bridge |
bundleFinishedLoading
1 | - (void)bundleFinishedLoading:(RCTBridge *)bridge { |
调用JS里的AppRegistry.runApplication
方法,RCTCxxBridge 初始化完成,调用RCTInstanceCallback中的onBatchComplete
方法,然后调用RCTUIManager
的_layoutAndMount
RCTUIManager
负责处理UI相关的一切事务,包括控件的创建、更新和动画等
setBridge
1 | - (void)setBridge:(RCTBridge *)bridge { |
registerRootView
1 | - (void)registerRootView:(RCTRootContentView *)rootView { |
_layoutAndMount
1 | - (void)_layoutAndMount { |
接下来会调用RCTUIManager createView:viewName:rootTag:props:
方法创建子视图
1 | RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag |
在导出视图的Manager中,如果需要暴露一些属性设置,一般会加RCT_EXPORT_VIEW_PROPERTY
宏,这个宏会把方法名加上propConfig_
的前缀
1 | - (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView { |
创建完视图后,开始设置每个视图的子视图(setChildren:reactTags:
),不断地循环 createView 和 setChildren 直到整个页面渲染完毕
RCTTouchHandler
手势事件的捕获者并把事件传递给事件分发者RCTEventDispatcher进行分发
初始化
1 | - (instancetype)initWithBridge:(RCTBridge *)bridge { |
当有点击事件时,RCTEventDispatcher
调用sendEvent:
方法,加入到事件队列中,开始调用dispatchEvent:
方法,调用JS对应方法1
[_bridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]]; // event 有view的reactTag,以便JS中查找到对应方法
enqueueJSCall: RCTEventEmitter.receiveTouches
,事实上是RN里RCTEventEmitter的监听事件负责分发
args: RCTNormalizeInputEventName(_eventName), _reactTouches, _changedIndexes
1 | - (void)sendEvent:(id<RCTEvent>)event { |
当有输入事件时,RCTEventDispatcher
则会调用sendTextEventWithType:reactTag:text:key:eventCount:
方法
1 | - (void)sendTextEventWithType:(RCTTextEventType)type |
从上面的代码可以看出输入事件和手势事件最终都是调用bridge enqueueJSCall:method:args:completion
方法,模块都是RCTEventEmitter,但是方法名一个是 receiveEvent,另一个是 receiveTouches。