ReactNative原理與核心知識點(diǎn)
React Native特點(diǎn)
跨平臺
使用js寫出頁面組件代碼被React框架統(tǒng)一轉(zhuǎn)成Virtual DOM樹,Virtual DOM樹是UI結(jié)構(gòu)的一層抽象,可以被轉(zhuǎn)換成任何支持端的UI視圖。
ReactNative框架將Virtual DOM 轉(zhuǎn)成原APP的UIView樹。
熱修復(fù)
ReactNative的產(chǎn)物bundle包,bundle包中包含的為RN業(yè)務(wù)所需要的所有資源,包括代碼和資源。bundle的加載方式是APP啟動(dòng)時(shí)從后臺下載,然后通過js虛擬機(jī)執(zhí)行的。
所以可以將每次業(yè)務(wù)迭代修改后的代碼上傳到服務(wù),進(jìn)行用戶無感知版本更新。
注意:
bundle中的業(yè)務(wù)代碼不能修改APP現(xiàn)有的原生行為,不能調(diào)用私有API,不然禁止上架。
bundle包中的js是經(jīng)過babel轉(zhuǎn)義后的普通js,而非jsx語法糖。

?
JS與Native交互的基本原理
JS引擎
iOS側(cè)使用的JavaScriptCore作為bundle產(chǎn)物的js執(zhí)行引擎。
JS與Native交互的基本原理很簡單,就是在JS的全局上下文添加成員變量。原生調(diào)用JS是JS在JS上下文中添加方法成員變量,然后原生調(diào)用。JS調(diào)用原生是原生往JS上下文中添加方法成員變量,然后JS調(diào)用。
JS調(diào)用原生
通過將block對象賦值給js全局上下文中的全局變量,js內(nèi)部調(diào)用這個(gè)全局方法進(jìn)行執(zhí)行。
ctx[
@"NativeMethod"
] = ^(NSString *name) {
????
// do something
????
return
?someResult
}
原生調(diào)用JS
先創(chuàng)建一個(gè)JS上下文對象,在上下文中添加方法的全局變量。原生獲取上下文的全局變量Value, 然后調(diào)用,執(zhí)行這個(gè)JS方法。
// 創(chuàng)建一個(gè)ctx的JS上下文
JSContent *ctx = [[JSContent alloc] init];
// 創(chuàng)建一個(gè)變量name
[ctx evaluateScript:
@"var name = 'jack'"
];
// 創(chuàng)建一個(gè)方法
[ctx evaluateScript:
@"var sayHi = function(name) { return 'hello ' + name }"
];
// 通過ctx上下文對象,獲取到hello方法
JSValue *sayHiUnction = ctx[
@"sayHi"
];
// 運(yùn)行js方法
JSValue *greetings = [sayHiUnction callWithArguments:@[
@"jack"
];?
// hello jack
ReactNative框架中原生與JS的調(diào)用基本思路也是這種,只不過考慮到大量的Native對象注冊會污染js引擎中的上下文,增加了一層Bridge。
原生和JS之間的交互都是通過Bridge這個(gè)通道,通過里面的幾個(gè)基礎(chǔ)方法進(jìn)行交互。原生與JS的交互是異步的。
另外,F(xiàn)acebook為了提升RN框架中JS的執(zhí)行效率,專門推出了一個(gè)JS引擎 Hermes, 在關(guān)鍵指標(biāo)上,相比于JSCore,V8提升了不少,比較易于RN框架集成。
?
ReactNative核心知識
RCTBridge:?ReactNative中原生與JS交互的通道
RCTBridge用于給js引擎提供原生擴(kuò)展接口。將原生功能如定位,3D等通過Bridge將其封裝成JS接口,然后注入到j(luò)s引擎的上下文中。
RN框架啟動(dòng)的簡單流程為:首先將js代碼加載到內(nèi)存,然后創(chuàng)建RCTBridge實(shí)例,然后創(chuàng)建RCTRootContentView內(nèi)容展示的容器視圖,然后調(diào)用JS上下文中AppRegistry對象的runApplication方法,并將@[moduleName, appParameters]組件名,參數(shù)傳遞給JS。
// RCTRootView.m
- (
void
)javaScriptDidLoad:(
NSNotification
?*)notification
{
??
RCTAssertMainQueue();
??
RCTBridge *bridge = notification.userInfo[@
"bridge"
];
??
if
?(bridge != _contentView.bridge) {
????
[
self
?bundleFinishedLoading:bridge];
??
}
}
- (
void
)bundleFinishedLoading:(RCTBridge *)bridge
{
??
// 省略創(chuàng)建RCTRootContentView...
??
[
self
?runApplication:bridge];
??
// 省略添加一個(gè)RCTRootContentView...
}
- (
void
)runApplication:(RCTBridge *)bridge
{
??
NSString
?*moduleName = _moduleName ?: @
""
;?
// 這里是@"NewProject"
??
NSDictionary
?*appParameters = @{
????
@
"rootTag"
: _contentView.reactTag,
????
@
"initialProps"
: _appProperties ?: @{},
??
};
??
[bridge enqueueJSCall:@
"AppRegistry"
?????????????????method:@
"runApplication"
???????????????????args:@[moduleName, appParameters]
?????????????
completion:
NULL
];
}
?
原生調(diào)用JS
在JS上下文中,調(diào)用JS的方式是通過方法:global.batchedBridge.callFunctionReturnFlushedQueue

?
所以RN在原生側(cè)的的JS引擎的封裝對象中使用成員變量保存了這個(gè)JS的函數(shù)指針,原生調(diào)用JS時(shí),通過傳遞參數(shù) moduleid 和 methodid 完成方法的調(diào)用。
void
?JSIExecutor
::
bindBridge
() {
??
std
::
call_once
(
bindFlag_
, [
this
] {
????
SystraceSection
?s
(
"JSIExecutor::bindBridge (once)"
);
????
Value
?batchedBridgeValue
?=
????????
runtime_
-
>
global
().
getProperty
(*
runtime_
,?
"__fbBatchedBridge"
);
????
if
?(
batchedBridgeValue
.
isUndefined
() || !
batchedBridgeValue
.
isObject
()) {
??????
throw
?JSINativeException
(
??????????
"Could not get BatchedBridge, make sure your bundle is packaged correctly"
);
????
}
????
Object
?batchedBridge
?=?
batchedBridgeValue
.
asObject
(*
runtime_
);
????
callFunctionReturnFlushedQueue_
?=?
batchedBridge
.
getPropertyAsFunction
(
????????
*
runtime_
,?
"callFunctionReturnFlushedQueue"
);
????
invokeCallbackAndReturnFlushedQueue_
?=?
batchedBridge
.
getPropertyAsFunction
(
????????
*
runtime_
,?
"invokeCallbackAndReturnFlushedQueue"
);
????
flushedQueue_
?=
????????
batchedBridge
.
getPropertyAsFunction
(*
runtime_
,?
"flushedQueue"
);
??
});
}
?
JS調(diào)用原生
JS調(diào)用原生通常是通過原生主動(dòng)處理_eventQueue中的事件,特殊情況會直接調(diào)用原生注冊給JS的nativeFlushQueueImmediate方法, 并傳遞moduleName 、methodName、callback 參數(shù)給這個(gè)方法完成調(diào)用。

?
void
?JSIExecutor
::
initializeRuntime
() {
??
SystraceSection
?s
(
"JSIExecutor::initializeRuntime"
);
??
runtime_
-
>
global
().
setProperty
(
??????
*
runtime_
,
??????
"nativeModuleProxy"
,
??????
Object
::
createFromHostObject
(
??????????
*
runtime_
,?
std
::
make_shared
<
NativeModuleProxy
>
(
nativeModules_
)));
??
runtime_
-
>
global
().
setProperty
(
??????
*
runtime_
,
??????
"nativeFlushQueueImmediate"
,
??????
Function
::
createFromHostFunction
(
??????????
*
runtime_
,
??????????
PropNameID
::
forAscii
(*
runtime_
,?
"nativeFlushQueueImmediate"
),
??????????
1
,
??????????
[
this
](
??????????????
jsi
::
Runtime
?&
,
??????????????
const
?jsi
::
Value
?&
,
??????????????
const
?jsi
::
Value
?*
args
,
??????????????
size_t
?count
) {
????????????
if
?(
count
?!=?
1
) {
??????????????
throw
?std
::
invalid_argument
(
??????????????????
"nativeFlushQueueImmediate arg count must be 1"
);
????????????
}
????????????
callNativeModules
(
args
[
0
],?
false
);
????????????
return
?Value
::
undefined
();
??????????
}));
??
runtime_
-
>
global
().
setProperty
(
??????
*
runtime_
,
??????
"nativeCallSyncHook"
,
??????
Function
::
createFromHostFunction
(
??????????
*
runtime_
,
??????????
PropNameID
::
forAscii
(*
runtime_
,?
"nativeCallSyncHook"
),
??????????
1
,
??????????
[
this
](
??????????????
jsi
::
Runtime
?&
,
??????????????
const
?jsi
::
Value
?&
,
??????????????
const
?jsi
::
Value
?*
args
,
??????????????
size_t
?count
) {?
return
?nativeCallSyncHook
(
args
,?
count
); }));
??
runtime_
-
>
global
().
setProperty
(
??????
*
runtime_
,
??????
"globalEvalWithSourceUrl"
,
??????
Function
::
createFromHostFunction
(
??????????
*
runtime_
,
??????????
PropNameID
::
forAscii
(*
runtime_
,?
"globalEvalWithSourceUrl"
),
??????????
1
,
??????????
[
this
](
??????????????
jsi
::
Runtime
?&
,
??????????????
const
?jsi
::
Value
?&
,
??????????????
const
?jsi
::
Value
?*
args
,
??????????????
size_t
?count
) {?
return
?globalEvalWithSourceUrl
(
args
,?
count
); }));
??
if
?(
runtimeInstaller_
) {
????
runtimeInstaller_
(*
runtime_
);
??
}
??
bool
?hasLogger
(
ReactMarker
::
logTaggedMarker
);
??
if
?(
hasLogger
) {
????
ReactMarker
::
logMarker
(
ReactMarker
::
CREATE_REACT_CONTEXT_STOP
);
??
}
}
Virtual DOM 虛擬DOM
?
虛擬DOM的特點(diǎn)
1.用于描述頁面的UI結(jié)構(gòu):在作用上虛擬DOM和普通的DOM是一樣的。
2.平臺無關(guān)性:虛擬DOM表示的UI結(jié)構(gòu)是對UI的一層抽象,它是平臺無關(guān)的。具體的UI渲染是交個(gè)具體的平臺渲染引擎進(jìn)行的,如iOS,安卓自身的渲染引擎。
虛擬DOM對標(biāo)簽的定義
虛擬DOM把標(biāo)簽分為2類:原子型標(biāo)簽,組合型標(biāo)簽。
原子型標(biāo)簽是平臺支持型的基礎(chǔ)標(biāo)簽,如果RCTView, RCTText。對應(yīng)瀏覽器頁面中,原子型標(biāo)簽有h1,li,div等。
組合型標(biāo)簽是用戶自定義的組件,它在虛擬DOM中對應(yīng)的是自定義標(biāo)簽構(gòu)造器函數(shù),頁面渲染時(shí)調(diào)用這個(gè)構(gòu)造函數(shù),創(chuàng)建一個(gè)實(shí)例,然后調(diào)用實(shí)例的render方法,組合型標(biāo)簽的render方法內(nèi)會把組合標(biāo)簽進(jìn)行拆解,最后拆解成基本的原子型標(biāo)簽。
var
?ele
?= {
????
...
????
type
:?
type
,?
// 元素的類型
????
key
:?
key
,?
// 元素key標(biāo)示
????
ref
:?
ref
,?
// 元素的引用
????
props
:?
props
,?
// 元素的參數(shù),包含children
????
...
}
// example 1
<
div
>
hello
<
/
div
>
// 會被描述為
{
type
: '
div
',
????
props
: {
????????
children
: ['
hello
']
????
}
}
// example 2
<
CustomerComponents
?/
>
// 會被描述為
{
????
type
:?
CustomerComponents
}
?
UI渲染
RN框架與瀏覽器的對比:在瀏覽器中,JS通過調(diào)用DOM API創(chuàng)建UI視圖。在RN中,JS通過調(diào)用RCTUIManager來創(chuàng)建iOS,Android移動(dòng)端的UI視圖。
RN的UI渲染是基于虛擬DOM的,通過根據(jù)不同的平臺調(diào)用不同平臺的Bridge, Brideg再調(diào)用不同平臺的的RCTUIManager進(jìn)行UI的創(chuàng)建。
?
其他
三條線程
RN內(nèi)部有三條線程在同時(shí)運(yùn)行著:Shadow Thread, JS Thread, UI Thread。
JS Thread:JS線程,負(fù)責(zé)JS與原生的交互,它們的交互是異步的,每次調(diào)用都是將block放入隊(duì)列中,等js代碼執(zhí)行完后,讀取事件隊(duì)列進(jìn)行處理。
UI Thread:UI主線程,負(fù)責(zé)頁面的交互與渲染, 由RCTUIManager使用。
Shadow Thread: 負(fù)責(zé)將flex布局轉(zhuǎn)成Native的布局,由yago引擎使用。
三個(gè)隊(duì)列
RN框架內(nèi),原生與JS的交換類型分兩種:UI和事件,這2這種事件的處理都是異步的,它們都是將事件順序放置到隊(duì)列中,在合適的時(shí)機(jī)被調(diào)用。
事件的處理在RCTBridge中處理,UI的處理在RCTUIManager中處理。
JS調(diào)用原生異步事件隊(duì)列:_eventQueue隊(duì)列
原生調(diào)用JS異步事件隊(duì)列:_pendingCalls隊(duì)列
UI更新異步事件處理隊(duì)列:_pendingUIBlocks隊(duì)列
JSI
javascript interface js虛擬機(jī)通用接口層,是針對JS引擎封裝的上層API框架,使用JSI做JS引擎調(diào)用的優(yōu)點(diǎn):
1.底部可以任意替換JS引擎而不影響上層JS引擎的使用。如:可以任意替換JavaScript Core, V8等。
2.通過JSI,JavaScript可以持有C++宿主對象的引用,所以可以直接調(diào)用原生方法(UIView, NativeModule),它與現(xiàn)在統(tǒng)一使用Bridge這個(gè)通道和消息異步調(diào)用比起來,提高了消息發(fā)送的及時(shí)性,避免了消息隊(duì)列執(zhí)行的等待。
React Native核心知識在框架中的使用
?
React Native核心功能在RN項(xiàng)目啟動(dòng)時(shí)會進(jìn)行各自的初始化,生成bundle運(yùn)行上下文。在類型上可以分為2類:
1.JS與原生的事件處理:創(chuàng)建RCTBridge橋接通道。
2.UI交互與更新的事件處理:創(chuàng)建RCTRootView容器視圖。
APP啟動(dòng),React Native運(yùn)行環(huán)境初始化。
- (
BOOL
)
application
:(
UIApplication
?*)
application
?didFinishLaunchingWithOptions
:(
NSDictionary
?*)
launchOptions
{
??
if
?(!
self
.
bridge
) {
????
self
.
bridge
?= [
self
?createBridgeWithDelegate
:
self
?launchOptions
:
launchOptions
];
??
}
??
NSDictionary
?*
initProps
?= [
self
?prepareInitialProps
];
??
UIView
?*
rootView
?= [
self
?createRootViewWithBridge
:
self
.
bridge
?moduleName
:
self
.
moduleName
?initProps
:
initProps
];
??
if
?(@
available
(
iOS
?13.0
, *)) {
????
rootView
.
backgroundColor
?= [
UIColor
?systemBackgroundColor
];
??
}?
else
?{
????
rootView
.
backgroundColor
?= [
UIColor
?whiteColor
];
??
}
??
self
.
window
?= [[
UIWindow
?alloc
]?
initWithFrame
:[
UIScreen
?mainScreen
].
bounds
];
??
UIViewController
?*
rootViewController
?= [
self
?createRootViewController
];
??
rootViewController
.
view
?=?
rootView
;
??
self
.
window
.
rootViewController
?=?
rootViewController
;
??
[
self
.
window
?makeKeyAndVisible
];
??
return
?YES
;
}
?
JS與原生的事件處理:創(chuàng)建RCTBridge橋接通道
RCTBridge的主要邏輯是在batchedBridge中,主要初始化流程為:
1.初始化Native Modules
2.創(chuàng)建Native Modules配置表
3.準(zhǔn)備JS引擎工廠,創(chuàng)建JS引擎
4.將Modules配置信息注冊到JS引擎中
5.加載boundle代碼
6.執(zhí)行boundle代碼
- (
void
)
setUp
{
??
RCT_PROFILE_BEGIN_EVENT
(
0
, @
"-[RCTBridge setUp]"
,?
nil
);
??
//_performanceLogger日志工具初始化
??
//_bundleURL獲取
??
//batchedBridge創(chuàng)建
??
self
.
batchedBridge
?= [[
bridgeClass
?alloc
]?
initWithParentBridge
:
self
];
??
[
self
.
batchedBridge
?start
];
??
RCT_PROFILE_END_EVENT
(
RCTProfileTagAlways
, @
""
);
}
batchedBridge
是
RCTCXXBridge
,它的初始化方法如下:
- (
instancetype
)
initWithParentBridge
:(
RCTBridge
?*)
bridge
{
??
RCTAssertParam
(
bridge
);
??
if
?((
self
?= [
super
?initWithDelegate
:
bridge
.
delegate
????????????????????????????
bundleURL
:
bridge
.
bundleURL
???????????????????????
moduleProvider
:
bridge
.
moduleProvider
????????????????????????
launchOptions
:
bridge
.
launchOptions
])) {
????
_parentBridge
?=?
bridge
;
????
_performanceLogger
?= [
bridge
?performanceLogger
];
????
registerPerformanceLoggerHooks
(
_performanceLogger
);
????
/**
?????
* Set Initial State
?????
*/
????
_valid
?=?
YES
;
????
_loading
?=?
YES
;
????
_moduleRegistryCreated
?=?
NO
;
????
_pendingCalls
?= [
NSMutableArray
?new
];
????
_displayLink
?= [
RCTDisplayLink
?new
];
????
_moduleDataByName
?= [
NSMutableDictionary
?new
];
????
_moduleClassesByID
?= [
NSMutableArray
?new
];
????
_moduleDataByID
?= [
NSMutableArray
?new
];
????
_objCModuleRegistry
?= [
RCTModuleRegistry
?new
];
????
[
_objCModuleRegistry
?setBridge
:
self
];
????
_bundleManager
?= [
RCTBundleManager
?new
];
????
[
_bundleManager
?setBridge
:
self
];
????
_viewRegistry_DEPRECATED
?= [
RCTViewRegistry
?new
];
????
[
_viewRegistry_DEPRECATED
?setBridge
:
self
];
????
_callableJSModules
?= [
RCTCallableJSModules
?new
];
????
[
_callableJSModules
?setBridge
:
self
];
????
[
RCTBridge
?setCurrentBridge
:
self
];
????
[[
NSNotificationCenter
?defaultCenter
]?
addObserver
:
self
?????????????????????????????????????????????
selector
:@
selector
(
handleMemoryWarning
)
?????????????????????????????????????????????????
name
:
UIApplicationDidReceiveMemoryWarningNotification
???????????????????????????????????????????????
object
:
nil
];
????
RCTLogSetBridgeModuleRegistry
(
_objCModuleRegistry
);
????
RCTLogSetBridgeCallableJSModules
(
_callableJSModules
);
??
}
??
return
?self
;
}
- (
void
)
start
{
??
RCT_PROFILE_BEGIN_EVENT
(
RCTProfileTagAlways
, @
"-[RCTCxxBridge start]"
,?
nil
);
??
[[
NSNotificationCenter
?defaultCenter
]?
postNotificationName
:
RCTJavaScriptWillStartLoadingNotification
??????????????????????????????????????????????????????
object
:
_parentBridge
????????????????????????????????????????????????????
userInfo
:@{@
"bridge"
?:?
self
}];
??
//啟動(dòng)JS線程
??
_jsThread
?= [[
NSThread
?alloc
]?
initWithTarget
:[
self
?class
]?
selector
:@
selector
(
runRunLoop
)?
object
:
nil
];
??
_jsThread
.
name
?=?
RCTJSThreadName
;
??
_jsThread
.
qualityOfService
?=?
NSOperationQualityOfServiceUserInteractive
;
#
if
?RCT_DEBUG
??
_jsThread
.
stackSize
?*=?
2
;
#
endif
??
[
_jsThread
?start
];
??
dispatch_group_t
?prepareBridge
?=?
dispatch_group_create
();
??
[
_performanceLogger
?markStartForTag
:
RCTPLNativeModuleInit
];
??
//1.初始化Native Modules
??
[
self
?registerExtraModules
];
??
//2.創(chuàng)建Native Modules配置表
??
// Initialize all native modules that cannot be loaded lazily
??
(
void
)[
self
?_initializeModules
:
RCTGetModuleClasses
()?
withDispatchGroup
:
prepareBridge
?lazilyDiscovered
:
NO
];
??
[
self
?registerExtraLazyModules
];
??
[
_performanceLogger
?markStopForTag
:
RCTPLNativeModuleInit
];
??
// This doesnt really do anything.? The real work happens in initializeBridge.
??
_reactInstance
.
reset
(
new
?Instance
);
??
__weak
?RCTCxxBridge
?*
weakSelf
?=?
self
;
??
// 3.準(zhǔn)備JS引擎工廠,創(chuàng)建JS引擎
??
std
::
shared_ptr
<
JSExecutorFactory
>
?executorFactory
;
??
if
?(!
self
.
executorClass
) {
????
if
?([
self
.
delegate
?conformsToProtocol
:@
protocol
(
RCTCxxBridgeDelegate
)]) {
??????
id
<
RCTCxxBridgeDelegate
>
?cxxDelegate
?= (
id
<
RCTCxxBridgeDelegate
>
)
self
.
delegate
;
??????
executorFactory
?= [
cxxDelegate
?jsExecutorFactoryForBridge
:
self
];
????
}
????
// 4.將Modules配置信息注冊到JS引擎中
????
if
?(!
executorFactory
) {
??????
auto
?installBindings
?=?
RCTJSIExecutorRuntimeInstaller
(
nullptr
);
#
if
?RCT_USE_HERMES
??????
executorFactory
?=?
std
::
make_shared
<
HermesExecutorFactory
>
(
installBindings
);
#
else
??????
executorFactory
?=?
std
::
make_shared
<
JSCExecutorFactory
>
(
installBindings
);
#
endif
????
}
??
}?
else
?{
????
id
<
RCTJavaScriptExecutor
>
?objcExecutor
?= [
self
?moduleForClass
:
self
.
executorClass
];
????
executorFactory
.
reset
(
new
?RCTObjcExecutorFactory
(
objcExecutor
, ^(
NSError
?*
error
) {
??????
if
?(
error
) {
????????
[
weakSelf
?handleError
:
error
];
??????
}
????
}));
??
}
????
//_turboModuleRegistry是一個(gè)TurboModule注冊表,TurboModule是JS在RN中的一種優(yōu)化方式,將常用的JS代碼編譯成可執(zhí)行代碼,提高執(zhí)行速度。
??
/**
???
* id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleRegistry> object to
???
* RCTCxxBridge If id<RCTTurboModuleRegistry> is assigned by this time, eagerly initialize all TurboModules
???
*/
??
if
?(
_turboModuleRegistry
?&
&
?RCTTurboModuleEagerInitEnabled
()) {
????
for
?(
NSString
?*
moduleName
?in
?[
_turboModuleRegistry
?eagerInitModuleNames
]) {
??????
[
_turboModuleRegistry
?moduleForName
:[
moduleName
?UTF8String
]];
????
}
????
for
?(
NSString
?*
moduleName
?in
?[
_turboModuleRegistry
?eagerInitMainQueueModuleNames
]) {
??????
if
?(
RCTIsMainQueue
()) {
????????
[
_turboModuleRegistry
?moduleForName
:[
moduleName
?UTF8String
]];
??????
}?
else
?{
????????
id
<
RCTTurboModuleRegistry
>
?turboModuleRegistry
?=?
_turboModuleRegistry
;
????????
dispatch_group_async
(
prepareBridge
,?
dispatch_get_main_queue
(), ^{
??????????
[
turboModuleRegistry
?moduleForName
:[
moduleName
?UTF8String
]];
????????
});
??????
}
????
}
??
}
??
// Dispatch the instance initialization as soon as the initial module metadata has
??
// been collected (see initModules)
??
dispatch_group_enter
(
prepareBridge
);
??
[
self
?ensureOnJavaScriptThread
:^{
????
[
weakSelf
?_initializeBridge
:
executorFactory
];
????
dispatch_group_leave
(
prepareBridge
);
??
}];
??
// 5.加載boundle代碼
??
// Load the source asynchronously, then store it for later execution.
??
dispatch_group_enter
(
prepareBridge
);
??
__block
?NSData
?*
sourceCode
;
??
[
self
??????
loadSource
:^(
NSError
?*
error
,?
RCTSource
?*
source
) {
????????
if
?(
error
) {
??????????
[
weakSelf
?handleError
:
error
];
????????
}
????????
sourceCode
?=?
source
.
data
;
????????
dispatch_group_leave
(
prepareBridge
);
??????
}
??????
onProgress
:^(
RCTLoadingProgress
?*
progressData
) { }];
??
// 模塊和js代碼加載完成后,執(zhí)行js代碼
??
// Wait for both the modules and source code to have finished loading
??
dispatch_group_notify
(
prepareBridge
,?
dispatch_get_global_queue
(
QOS_CLASS_USER_INTERACTIVE
,?
0
), ^{
????
RCTCxxBridge
?*
strongSelf
?=?
weakSelf
;
????
if
?(
sourceCode
?&
&
?strongSelf
.
loading
) {
??????
// 6.執(zhí)行boundle代碼
??????
[
strongSelf
?executeSourceCode
:
sourceCode
?sync
:
NO
];
????
}
??
});
??
RCT_PROFILE_END_EVENT
(
RCTProfileTagAlways
, @
""
);
}
?
初始化Native Modules與創(chuàng)建Native Modules配置表
把本地的RN模塊都收集起來,包括RN框架自帶的和用戶自定義的,將模塊信息保存到Bridge的變量中,用于與JS交換。
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
?
JS發(fā)送消息到Native時(shí),通過- (id)moduleForName:(const char *)moduleName;查詢到模塊詳情,進(jìn)行模塊調(diào)用。
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
準(zhǔn)備JS引擎工廠,創(chuàng)建JS引擎與將Modules配置信息注冊到JS引擎中
RN將Native Modules信息收集完成后保存到成員變量中,這個(gè)成員變量是一個(gè)數(shù)組。使用moduleConfig保存模塊的模塊名,方法名。然后將這些數(shù)據(jù)注入到JS引擎中。
JS調(diào)用原生時(shí),通過模塊名,方法名,參數(shù)調(diào)用原生方法。在原生調(diào)用JS時(shí),會將調(diào)用放入_pendingCalls隊(duì)列中,進(jìn)行異步執(zhí)行。而JS調(diào)原生是將調(diào)用放入到_eventQueue隊(duì)列中,進(jìn)行異步執(zhí)行。
JS可以通過方法nativeFlushQueueImmediate直接調(diào)用Native,但是一般JS不會這樣做,而是等原生自己去_eventQueue隊(duì)列中自己去取任務(wù)做處理。
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
- (
void
)
flushEventsQueue
{
??
[
_eventQueueLock
?lock
];
??
NSDictionary
?*
events
?=?
_events
;
??
_events
?= [
NSMutableDictionary
?new
];
??
NSMutableArray
?*
eventQueue
?=?
_eventQueue
;
??
_eventQueue
?= [
NSMutableArray
?new
];
??
_eventsDispatchScheduled
?=?
NO
;
??
[
_eventQueueLock
?unlock
];
??
for
?(
NSNumber
?*
eventId
?in
?eventQueue
) {
????
[
self
?dispatchEvent
:
events
[
eventId
]];
??
}
}
?
UI交互與更新的事件處理:創(chuàng)建RCTRootView容器視圖
?
RCTRootView為RN頁面的入口,在RCTRootView初始化過程中,會創(chuàng)建RCTRootContentView作為內(nèi)容視圖放置在RCTRootView的底部作為根視圖。
RCTRootContentView的初始化方法中,在uiManager中將RCTRootContentView注冊成根視圖。
- (
instancetype
)
initWithFrame
:(
CGRect
)
frame
???????????????????????
bridge
:(
RCTBridge
?*)
bridge
?????????????????????
reactTag
:(
NSNumber
?*)
reactTag
???????????????
sizeFlexiblity
:(
RCTRootViewSizeFlexibility
)
sizeFlexibility
{
??
if
?((
self
?= [
super
?initWithFrame
:
frame
])) {
????
_bridge
?=?
bridge
;
????
self
.
reactTag
?=?
reactTag
;
????
_sizeFlexibility
?=?
sizeFlexibility
;
????
_touchHandler
?= [[
RCTTouchHandler
?alloc
]?
initWithBridge
:
_bridge
];
????
[
_touchHandler
?attachToView
:
self
];
????
[
_bridge
.
uiManager
?registerRootView
:
self
];
??
}
??
return
?self
;
}
RCTUIManager是RN中UI的管理者,它負(fù)責(zé)處理所有與UI相關(guān)的事情,如:注入到JS中的創(chuàng)建View方法。
在createView方法中可以看到,RN對View的操作都是雙份的,分別作用在RCTShadowView和UIView上。RCTShadowView和UIView的關(guān)系類似于虛擬DOM和DOM的關(guān)系。
RCTShadowView是一個(gè)虛擬DOM樹,是一個(gè)結(jié)構(gòu)體,用于描述視圖的樣式和事件,比較輕量級。
在RN中當(dāng)調(diào)用setState更新組件狀態(tài)時(shí),就會生成一個(gè)新的虛擬DOM,然后RN將新的虛擬DOM與舊的虛擬DOM進(jìn)行Diff對比,生成差異對象,然后遍歷差異對象,將所有的改動(dòng)更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合適的時(shí)候再統(tǒng)一更新到UI上。
UI更新操作也是異步的,更新任務(wù)被放置在_pendingUIBlocks隊(duì)列上,在UI變化時(shí)或Bridge批出來結(jié)束時(shí)刷新這個(gè)隊(duì)列。
RCT_EXPORT_METHOD
(
createView
??????????????????
: (
nonnull
?NSNumber
?*)
reactTag
?viewName
??????????????????
: (
NSString
?*)
viewName
?rootTag
??????????????????
: (
nonnull
?NSNumber
?*)
rootTag
?props
??????????????????
: (
NSDictionary
?*)
props
)
{
??
RCTComponentData
?*
componentData
?=?
_componentDataByName
[
viewName
];
??
if
?(
componentData
?==?
nil
) {
????
RCTLogError
(@
"No component found for view with name \"%@\""
,?
viewName
);
??
}
??
// Register shadow view
??
RCTShadowView
?*
shadowView
?= [
componentData
?createShadowViewWithTag
:
reactTag
];
??
if
?(
shadowView
) {
????
[
componentData
?setProps
:
props
?forShadowView
:
shadowView
];
????
_shadowViewRegistry
[
reactTag
] =?
shadowView
;
????
RCTShadowView
?*
rootView
?=?
_shadowViewRegistry
[
rootTag
];
????
RCTAssert
(
????????
[
rootView
?isKindOfClass
:[
RCTRootShadowView
?class
]] || [
rootView
?isKindOfClass
:[
RCTSurfaceRootShadowView
?class
]],
????????
@
"Given `rootTag` (%@) does not correspond to a valid root shadow view instance."
,
????????
rootTag
);
????
shadowView
.
rootView
?= (
RCTRootShadowView
?*)
rootView
;
??
}
??
// Dispatch view creation directly to the main thread instead of adding to
??
// UIBlocks array. This way, it doesnt get deferred until after layout.
??
__block
?UIView
?*
preliminaryCreatedView
?=?
nil
;
??
void
?(^
createViewBlock
)(
void
) = ^{
????
// Do nothing on the second run.
????
if
?(
preliminaryCreatedView
) {
??????
return
;
????
}
????
preliminaryCreatedView
?= [
componentData
?createViewWithTag
:
reactTag
?rootTag
:
rootTag
];
????
if
?(
preliminaryCreatedView
) {
??????
self
-
>
_viewRegistry
[
reactTag
] =?
preliminaryCreatedView
;
????
}
??
};
??
// We cannot guarantee that asynchronously scheduled block will be executed
??
// *before* a block is added to the regular mounting process (simply because
??
// mounting process can be managed externally while the main queue is
??
// locked).
??
// So, we positively dispatch it asynchronously and double check inside
??
// the regular mounting block.
??
RCTExecuteOnMainQueue
(
createViewBlock
);
??
[
self
?addUIBlock
:^(
__unused
?RCTUIManager
?*
uiManager
,?
__unused
?NSDictionary
<
NSNumber
?*,?
UIView
?*
>
?*
viewRegistry
) {
????
createViewBlock
();
????
if
?(
preliminaryCreatedView
) {
??????
[
componentData
?setProps
:
props
?forView
:
preliminaryCreatedView
];
????
}
??
}];
??
[
self
?_shadowView
:
shadowView
?didReceiveUpdatedProps
:[
props
?allKeys
]];
}