react native 0.50 源码解析 再出发 持续更新
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react native 0.50 源码解析 再出发 持续更新相关的知识,希望对你有一定的参考价值。
1.核心类
1.1 RCTRootView
一个RCTRootView持有一个RCTBridge成员变量
RCTRootView : UIView
RCTBridge *bridge;
UIViewController *reactViewController;
UIView *contentView;
UIView *loadingView;
1.2 RCTBridge
一个RCTBridge持有一个RCTCxxBridge成员变量
RCTBridge.h
@interface RCTBridge : NSObject <RCTInvalidating>
RCTBridge+Private.h
@interface RCTBridge ()
RCTBridge *batchedBridge;
@end
RCTBridge.m
- (Class)bridgeClass
return [RCTCxxBridge class];
- (void)setUp
Class bridgeClass = self.bridgeClass;
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start];
1.3 RCTCxxBridge
RCTCxxBridge继承自RCTBridge,是RCTBridge的一个变量,拥有弱引用parentBridge指向RCTBridge变量
RCTBridge+Private.h
@interface RCTCxxBridge:RCTBridge
RCTCxxBridge.mm
@interface RCTCxxBridge()
RCTBridge *parentBridge;
@end
//@interface ViewController()后面 ,@end前面的是类扩展,就是创建本类中似有的属性和方法。其他类不能使用的
2.核心流程
2.1 native注册模块表
一个native类需要向js暴露的过程,就是向全局静态数组RCTModuleClasses注册自身class的过程。工程启动后,自动生成全局静态数组RCTModuleClasses,储存注册的class
######RCTBridge
声明全局静态数组:
static NSMutableArray<Class> *RCTModuleClasses;
######native类
需要做到以下两点,
1.实现RCTBridgeModule协议
2.在m文件中放置RCT_EXPORT_MODULE()代码。
RCT_EXPORT_MODULE宏给native类置入load函数。native类在加载过程中,可以自动向RCTModuleClasses注册自身Class。实例如下
ClassA.h
#import <React/RCTBridgeModule.h>
@interface ClassA <RCTBridgeModule>
@end
ClassA.m
RCT_EXPORT_MODULE() ==>
extern __attribute__((visibility("default")))
void RCTRegisterModule(Class);
+ (void)load { RCTRegisterModule(self); }
========================================
宏定义
RCTBridgeModule.h
#define RCT_EXPORT_MODULE(js_name) RCT_EXTERN void RCTRegisterModule(Class); + (NSString *)moduleName { return @#js_name; } + (void)load { RCTRegisterModule(self); }
#define RCT_EXPORT_MODULE(js_name) RCT_EXTERN void RCTRegisterModule(Class); + (NSString *)moduleName { return @#js_name; } + (void)load { RCTRegisterModule(self); }
RCTDefindes.h
#define RCT_EXTERN extern __attribute__((visibility("default")))
注册
RCTBridge.m
static NSMutableArray<Class> *RCTModuleClasses;
void RCTRegisterModule(Class moduleClass)
[RCTModuleClasses addObject:moduleClass];
2.2 RCTCxxBridge初始化模块表
RCTCxxBridge有三个成员变量,_moduleDataByName,_moduleDataByID,_moduleClassesByID
RCTCxxBridge
NSMutableDictionary<NSString *, RCTModuleData *> *_moduleDataByName; //字典,name:RCTModuleData键值对
NSMutableArray<RCTModuleData *> *_moduleDataByID; //RCTModuleData数组
NSMutableArray<Class> *_moduleClassesByID; //Class数组
- (void)start
[self _initModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
- (void)_initModules:(NSArray<id<RCTBridgeModule>> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
NSArray<RCTModuleData *> *moduleDataById = [self registerModulesForClasses:modules]; //将modules转换成RCTCxxBridge的模块组
- (NSArray<RCTModuleData *> *)registerModulesForClasses:(NSArray<Class> *)moduleClasses
for (Class moduleClass in moduleClasses) {
_moduleDataByName[moduleName] = moduleData;
[_moduleClassesByID addObject:moduleClass];
[moduleDataByID addObject:moduleData];
}
[_moduleDataByID addObjectsFromArray:moduleDataByID];
2.3 RCTCxxBridge 加载/执行JS资源
- (void)start
[self loadSource:^(NSError *error, RCTSource *source)
{sourceCode = source.data;}] // 加载JS
[strongSelf executeSourceCode:sourceCode sync:NO]; //执行JS
- (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad onProgress:(RCTSourceLoadProgressBlock)onProgress
发送通知RCTBridgeWillDownloadScriptNotification
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, RCTSource *source)
{
发送通知RCTBridgeDidDownloadScriptNotification:RCTSource
}
[RCTjavascriptLoader loadBundleAtURL:self.bundleURL onProgress:onProgress onComplete:^(NSError *error, RCTSource *source)
{
onSourceLoad(error, source);
}
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
- (void)executeApplicationScript:(NSData *)script
url:(NSURL *)url
async:(BOOL)async
dispatch_block_t completion = ^{
[self _flushPendingCalls];
广播通知RCTJavaScriptDidLoadNotification
[self ensureOnJavaScriptThread:^{
[self->_displayLink addToRunLoop:[NSRunLoop currentRunLoop]];
}];
}
- (void)executeApplicationScript:(NSData *)script
url:(NSURL *)url
async:(BOOL)async
if (isRAMBundle(script))
self->_reactInstance->loadRAMBundle(std::move(registry), std::move(scriptStr),
sourceUrlStr.UTF8String, !async);
else if (self->_reactInstance)
self->_reactInstance->loadScriptFromString(std::make_unique<NSDataBigString>(script),
sourceUrlStr.UTF8String, !async);
- (void)registerAdditionalModuleClasses:(NSArray<Class> *)modules
|=== - (NSArray<RCTModuleData *> *)registerModulesForClasses:(NSArray<Class> *)moduleClasses
|---for (Class moduleClass in moduleClasses)
{
NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);
moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];
}
[RCTCxxBridge registerExtraModules]
- (void)start
|--- 广播通知---
RCTJavaScriptWillStartLoadingNotification:@{@"bridge": self}
|--- 新建并启动js线程 _jsThread
|--- 注册moduleClass---
[self registerExtraModules];
[self _initModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
|--- 创建工厂并配置---
std::shared_ptr<JSExecutorFactory> executorFactory;
|--- 加载js资源---
[self loadSource:^(NSError error, RCTSource source)
|--- 执行js---
[strongSelf executeSourceCode:sourceCode sync:NO];
启动
[AppDelegate didFinishLaunchingWithOptions]
|---获取jsCodeLocation
|---[RCTRootView initWithBundleURL..]
|---[RCTBridge alloc+init]
|---设置变量:delegate,bundleURL,moduleProvider, launchOptions
|---[RCTBridge setUp]
|---设置变量:bundleURL
|---[RCTCxxBridge alloc+init] --> RCTBridge.batchedBridge
|---[RCTCxxBridge start]
|---[RCTRootView initWithBridge:...]
|---注册三个通知,如下
|---[self bundleFinishedLoading:([_bridge batchedBridge] ?: _bridge)];
|---RCTRootContentView alloc
|---[self runApplication:bridge];
|---[bridge enqueueJSCall:@"AppRegistry"
method:@"runApplication"
args:@[moduleName, appParameters]
completion:NULL];
|---insertSubview:_contentView
RCTRootView 继承自UIView,内含RCTBridge变量,初始化参数BundleURL/moduleName/Properties/launchOptions。初始化的时候,初始化RCTBridge变量,自我初始化。自我初始化过程:注册三个通知,RCTJavaScriptWillStartLoadingNotification/RCTJavaScriptDidLoadNotification/RCTContentDidAppearNotification
RCTBridge native call js
[RCTBridge enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion]
|---[self.batchedBridge enqueueJSCall:module method:method args:args completion:completion];
RCTCxxBridge 成员:
_moduleClassesByID
_moduleDataByID
_moduleDataByName 可变字典:string(moduleName):RCTModuleData(Data)的键值队
RCTCxxBridge native call js,可以从任意线程中调起
RCTBridge
- (void)setUp
URL转换
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start];
- (void)registerAdditionalModuleClasses:(NSArray<Class> *)modules
[self.batchedBridge registerAdditionalModuleClasses:modules];
RCTJavaScriptLoader
+ (void)loadBundleAtURL:(NSURL *)scriptURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)onComplete
|---NSData *data = [self attemptSynchronousLoadOfBundleAtURL:scriptURL
runtimeBCVersion:JSNoBytecodeFileFormatVersion
sourceLength:&sourceLength
error:&error];
|---onComplete(nil, RCTSourceCreate(scriptURL, data, sourceLength));
+ (NSData *)attemptSynchronousLoadOfBundleAtURL:(NSURL *)scriptURL
runtimeBCVersion:(int32_t)runtimeBCVersion
sourceLength:(int64_t *)sourceLength
error:(NSError **)error
|---FILE *bundle = fopen(scriptURL.path.UTF8String, "r");
size_t readResult = fread(&header, sizeof(header), 1, bundle);
[RCTCxxBridge enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion]
|---
NSInvocation 调用 native 方法
调用原生代码生成UI控件
RCTUIManager RCT_EXPORT_METHOD(createView:(nonnull NSNumber *)reactTag
viewName:(NSString *)viewName
rootTag:(nonnull NSNumber *)rootTag
props:(NSDictionary *)props)
|---createViewBlock
|---[RCTComponentData createViewWithTag:reactTag];
|--- [self.manager view]
#此处self.manager是实际需要生成的UI类,如RCTTextView等
#所以,所有RN的原生UI都必须实现view功能
|---RCTUIManager->_viewRegistry[reactTag] = preliminaryCreatedView;
#保存至注册数组,reactTag:preliminaryCreatedView
#RCTUIManager->_viewRegistry
# {1 = "<RCTRootContentView: 0x7faf12c015d0; reactTag: 1; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x60000024eaf0>; layer = <CALayer: 0x6040002376c0>>";
# 2 = "<UIView: 0x7faf12f0a6d0; frame = (0 0; 0 0); layer = <CALayer: 0x600000621300>>";}
Instance C++ 类
void Instance::loadRAMBundle(std::unique_ptr<RAMBundleRegistry> bundleRegistry,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL,
bool loadSynchronously)
RCTModuleMethod C++ 类
通过NSInvocation动态调用相应的OC模块方法
- (id)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments
[self processMethodSignature]; //set blocks
block(bridge, index, RCTNilIfNull(json(arguments)) //set arguments
[_invocation invokeWithTarget:module]; //调起函数
[_invocation getReturnValue:&returnValue]; //获取返回值
RN 支持功能
tabbar,nagigator,text,image,数据库?,ar?,真机调试日志?,网络,摄像头拍照,图片剪裁,音频,视频,下载,
添加RN库
tabbar
官方组件 TabBarios,TabBarIOS.Item,只支持iOS,弃用
流行组件
以上是关于react native 0.50 源码解析 再出发 持续更新的主要内容,如果未能解决你的问题,请参考以下文章
【原创】react-源码解析 - Component&Ref(3)
[技术博客]React Native——HTML页面代码高亮&数学公式解析
React Native Android入门实战及深入源码分析系列——React Native源码编译
React Native 源码分析——Native View创建流程
无法从“App.js”解析“@react-navigation/native”-React Native + 如何解决?