最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解React Native核心原理(React Native的橋接(Bridge)

 更新時間:2021年04月06日 11:04:12   作者:Gavell  
這篇文章主要介紹了深入理解React Native核心原理(React Native的橋接(Bridge),本文重點給大家介紹React Native的基礎(chǔ)知識及實現(xiàn)原理,需要的朋友可以參考下

在這篇文章之前我們假設(shè)你已經(jīng)了解了React Native的基礎(chǔ)知識,我們會重點關(guān)注當native和JavaScript進行信息交流時的內(nèi)部運行原理。

主線程

在開始之前,我們需要知道在React Native中有三個主要的線程:

  • shadow queue:負責布局工作
  • main thread:UIKit 在這個線程工作(譯者注:UI Manager線程,可以看成主線程,主要負責頁面交互和控件繪制的邏輯)
  • JavaScript thread:運行JS代碼的線程

另外,一般情況下每個native模塊都有自己的GCD隊列,除非有特殊說明(后面會解釋)

*shadow queue其實更像一個GCD隊列而不是線程

Native模塊

如果你還不知道怎么創(chuàng)建一個Native模塊,我推薦你去閱讀一下文檔

這是一個native模塊Person的例子,它既受JavaScript的調(diào)用,也可以調(diào)用JavaScript

@interface Person : NSObject <RCTBridgeModule> 
@end 
@implementation Logger 
RCT_EXPORT_MODULE() 
RCT_EXPORT_METHOD(greet:(NSString *)name) 
{ 
 NSLog(@"Hi, %@!", name); 
 [_bridge.eventDispatcher sendAppEventWithName:@"greeted" body:@{ @"name": name }];     
} 
@end

我們重點關(guān)注RCT_EXPORT_MODULE和RCT_EXPORT_METHOD這兩個宏,它們擴展成什么,它們的角色是什么,它們是如何運行的。

RCT_EXPORT_MODULE([js_name])

正如這個方法的名字那樣,它export出你的module,但是在這個特定的上下文中export是什么意思呢,它意味著橋接知道你的模塊。

它的定義實際上非常簡單:

#define RCT_EXPORT_MODULE(js_name) \ 
 RCT_EXTERN void RCTRegisterModule(Class); \ 
 + (NSString \*)moduleName { return @#js_name; } \ 
 + (void)load { RCTRegisterModule(self); }

它做了以下工作:

  • 首先聲明RCTRegisterModule為外部函數(shù),意味著這個函數(shù)的實現(xiàn)對于編譯器不可見,但是在鏈接階段可用
  • 聲明一個方法moduleName,返回可選的宏參數(shù)js_name,這樣這個模塊在JS中具有和Objective-C中不一樣的類名
  • 聲明一個load方法(當app加載到內(nèi)存中后,每個類的load方法都會被調(diào)用),load方法調(diào)用RCTRegisterModule,然后橋接才知道這個暴露出來的模塊

RCT_EXPORT_METHOD(method)

這個宏更有趣,它沒有在你的method中增加任何東西,除了聲明指定的方法外,它還創(chuàng)建了一個新方法。新方法如下所示:

+ (NSArray *)__rct_export__120 
{ 
 return @[ @"", @"log:(NSString *)message" ];
}

它是通過將前綴(__rct_export__)和可選的js_name(本例子為空)和聲明的行號以及__COUNTER__宏構(gòu)成。

這個方法的目的是返回一個包含可選js_name和method簽名的數(shù)組,這個js_name的作用是避免方法命名沖突。

Runtime

這整個設(shè)置僅僅是為了給橋接提供信息,讓它可以找到export出來的所有東西,modules和methods,但是這些都是在加載的時候發(fā)生的,現(xiàn)在我們來看看運行的時候是怎么使用的。

這是橋接初始化時的依賴關(guān)系圖:

初始化模塊

RCTRegisterModule所做的事就是把類推進數(shù)組,這樣在實例化一個新的橋接的時候就能找到這個類。橋接遍歷數(shù)組中的所有模塊,為每個模塊創(chuàng)建一個實例,在橋接那邊存儲一個實例的引用,同時給這個模塊實例一個橋接的引用(所以我們能兩邊都互相調(diào)用),然后檢查這個模塊實例是否有指定要在哪個隊列運行,否則給它一個新隊列,與其他模塊分開:

NSMutableDictionary *modulesByName; // = ... 
for (Class moduleClass in RCTGetModuleClasses()) { 
// ... 
 module = [moduleClass new]; 
 if ([module respondsToSelector:@selector(setBridge:)]){
 module.bridge = self;
 modulesByName[moduleName] = module; 
 // ... 
}

配置模塊

一旦我們有了這些modules,在后臺線程中,我們列出每個module的所有methods,然后調(diào)用以__rct__export__開頭的methods,我們得到一個method簽名的字符串。這很重要因為我們現(xiàn)在知道了參數(shù)的實際類型,在運行的時候我們只知道其中一個參數(shù)是id,但是通過這個途徑我們可以知道這個id實際上是NSString *

unsigned int methodCount; 
Method *methods = class_copyMethodList(moduleClass, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
 Method method = methods[i];
 SEL selector = method_getName(method);
 if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
 IMP imp = method_getImplementation(method);
 NSArray *entries = ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector);
 //...
 [moduleMethods addObject:/* Object representing the method */];
 }
}

設(shè)置JavaScript執(zhí)行器

JS執(zhí)行器有一個 -setUp 方法允許它做更復雜的工作,例如在后臺線程初始化JS代碼,這同時節(jié)約了一些工作,因為只有活躍的執(zhí)行器會接受 setUp 方法的調(diào)用,而不是所有的執(zhí)行器:

JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
_context = [[RCTJavaScriptContext alloc] initWithJSContext:ctx];

注入JSON配置

JSON配置僅包含我們的module,例如:

這個配置信息作為全局變量存儲在JavaScript虛擬機,所以當JS那邊的橋接初始化后它可以用這個信息來創(chuàng)建modules

加載JavaScript代碼 

這非常直觀,只需要從指定的任何提供程序中加載源代碼,通常在開發(fā)過程中從打包程序中加載源代碼,在生產(chǎn)環(huán)境中從磁盤加載。

執(zhí)行JavaScript代碼

一旦所有事情準備就緒,我們可以在JS虛擬機中加載應(yīng)用的源代碼,復制代碼,解析并執(zhí)行它。在第一次執(zhí)行時需要注冊所有CommonJS模塊并且需要入口文件。

JSValueRef jsError = NULL; 
JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script); 
JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString); 
JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError); 
JSStringRelease(jsURL); 
JSStringRelease(execJSString);

JavaScript中的Modules

在JS側(cè)我們現(xiàn)在可以通過react-native的NativeModules拿到前面的JSON配置信息構(gòu)成的module:

它運行的方式是當你調(diào)用一個方法的時候它被放到一個隊列,包括module的名稱,method的名稱以及所有的參數(shù),在JavsScript執(zhí)行的最后這個隊列會給原生模塊執(zhí)行。

調(diào)用周期

現(xiàn)在如果我們用上面的代碼調(diào)用module,它將會是這個樣子的:

調(diào)用必須從native開始,native調(diào)用JS(這張圖只是截取了JS運行的某個時刻),在執(zhí)行過程中,因為JS調(diào)用NativeModules的方法,它把這個調(diào)用入隊,因為這個調(diào)用必須在原生那邊執(zhí)行。當JS執(zhí)行完后,原生模塊遍歷入隊的所有調(diào)用,然后當它執(zhí)行這些調(diào)用后,通過橋接進行回調(diào)(一個原生模塊可以通過_bridge實例來調(diào)用enqueueJSCall:args:),來再次回調(diào)JS。

(如果您一直在關(guān)注該項目,過去也有來自native-> JS的調(diào)用隊列,該調(diào)用隊列會在每個vSYNC上分派,但為了縮短啟動時間已將其刪除)

參數(shù)類型

native到JS的調(diào)用很容易,參數(shù)被NSArray傳遞,我們將其編碼為JSON數(shù)據(jù),但是對于JS對native的調(diào)用,我們需要native的類型,為此我們檢查基本類型(ints,floats,chars...)但是就像上邊提及那樣,對于任何對象(結(jié)構(gòu)),運行時我們不會從NSMthodSignature獲得足夠的信息,所以我們把類型保存為字符串。

我們使用正則表達式從method簽名中提取類型,并使用RCTConvert類來實際轉(zhuǎn)換對象,默認情況下它為每種類型都提供了方法,并且嘗試將JSON輸入轉(zhuǎn)換為所需要的類型。

除非是一個struct,否則我們使用objc_msgSend動態(tài)調(diào)用該方法,因為arm64上沒有objc_msgSend_stret的版本,因此我們使用NSInvocation。

轉(zhuǎn)換完所有參數(shù)后,我們將使用另一個NSInvocation來調(diào)用目標module和method。

例子:

// If you had the following method in a given module, e.g. `MyModule`
RCT_EXPORT_METHOD(methodWithArray:(NSArray *) size:(CGRect)size) {}
// And called it from JS, like: 
require('NativeModules').MyModule.method(['a', 1], {
 x: 0, 
 y: 0, 
 width: 200, 
 height: 100 
});
// The JS queue sent to native would then look like the following:
// ** Remember that it's a queue of calls, so all the fields are arrays ** 
@[ 
 @[ @0 ], // module IDs 
 @[ @1 ], // method IDs 
 @[ // arguments 
 @[ 
 @[@"a", @1], 
 @{ @"x": @0, @"y": @0, @"width": @200, @"height": @100 } 
 ] 
 ]
];
// This would convert into the following calls (pseudo code) 
NSInvocation call 
call[args][0] = GetModuleForId(@0) 
call[args][1] = GetMethodForId(@1) 
call[args][2] = obj_msgSend(RCTConvert, NSArray, @[@"a", @1]) 
call[args][3] = NSInvocation(RCTConvert, CGRect, @{ @"x": @0, ... })
call()

線程

正如以上提及那樣,每個module默認都有一個GCD隊列,除非它通過實現(xiàn)-methodQueue方法或?qū)ethodQueue屬性與有效隊列合并來指定要在哪個隊列運行。ViewManagers*是例外(擴展了RCTViewManager),將默認使用Shadow Queue,而特殊目標RCTJSThread僅是一個占位符,因為它是線程而不是隊列。

(其實View Managers不是真正的例外,因為基類顯式的將Shadow Queue指定為目標隊列了)

當前線程規(guī)則如下:

  • -init和-setBridge:保證在主線程執(zhí)行
  • 所有export的方法保證在目標隊列執(zhí)行
  • 如果你實現(xiàn)了RCTInvalidating協(xié)議,則還可以確保在目標隊列上調(diào)用了invalidate
  • 無法保證在哪個線程調(diào)用-dealloc

當接收到JS的一批調(diào)用時,這些調(diào)用會按目標隊列進行分組,并行調(diào)用:

// group `calls` by `queue` in `buckets` 
for (id queue in buckets) { 
 dispatch_block_t block = ^{ 
 NSOrderedSet *calls = [buckets objectForKey:queue]; 
 for (NSNumber *indexObj in calls) { 
 // Actually call 
 } 
 }; 
 if (queue == RCTJSThread) { 
 [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; 
 } else if (queue) { 
 dispatch_async(queue, block); 
 } 
}

結(jié)尾

這就是React Native橋接工作原理的更深入概述。我希望者對想要構(gòu)建更復雜modules或者想對核心框架有貢獻的人有所幫助。

到此這篇關(guān)于深入理解React Native核心原理(React Native的橋接(Bridge)的文章就介紹到這了,更多相關(guān)React Native原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React.InputHTMLAttributes實踐和注意事項

    React.InputHTMLAttributes實踐和注意事項

    文章討論了如何在封裝組件中使用React.InputHTMLAttributes,以及如何通過 {...inputProps} 動態(tài)傳遞屬性,最后,文章總結(jié)了使用React.InputHTMLAttributes的最佳實踐和注意事項,感興趣的朋友一起看看吧
    2024-11-11
  • react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼

    react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼

    這篇文章主要介紹了react 權(quán)限樹形結(jié)構(gòu)實現(xiàn)代碼,項目背景react + ant design,本文結(jié)合實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧
    2024-05-05
  • React模擬實現(xiàn)Vue的keepAlive功能

    React模擬實現(xiàn)Vue的keepAlive功能

    Vue中,keep-alive組件可以緩存組件狀態(tài),在路由切換時重新掛載,實現(xiàn)這一功能在React中并不簡單,但我們可以借助一個第三方庫——react-activation 來模擬Vue的keep-alive功能,需要的朋友可以參考下
    2024-10-10
  • 使用react-virtualized實現(xiàn)圖片動態(tài)高度長列表的問題

    使用react-virtualized實現(xiàn)圖片動態(tài)高度長列表的問題

    一般我們在寫react項目中,同時渲染很多dom節(jié)點,會造成頁面卡頓, 空白的情況。為了解決這個問題,今天小編給大家分享一篇教程關(guān)于react-virtualized實現(xiàn)圖片動態(tài)高度長列表的問題,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • React?Immutable使用方法詳細介紹

    React?Immutable使用方法詳細介紹

    Immutable.js出自Facebook,是最流行的不可變數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)之一。它實現(xiàn)了完全的持久化數(shù)據(jù)結(jié)構(gòu),使用結(jié)構(gòu)共享。所有的更新操作都會返回新的值,但是在內(nèi)部結(jié)構(gòu)是共享的,來減少內(nèi)存占用
    2022-09-09
  • 淺談React之狀態(tài)(State)

    淺談React之狀態(tài)(State)

    這篇文章主要介紹了淺談React之狀態(tài)(State),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • 記錄React使用connect后,ref.current為null問題及解決

    記錄React使用connect后,ref.current為null問題及解決

    記錄React使用connect后,ref.current為null問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 在React中實現(xiàn)Vue的插槽功能的示例代碼

    在React中實現(xiàn)Vue的插槽功能的示例代碼

    在?Vue?中,插槽(Slots)允許父組件向子組件傳遞?HTML?結(jié)構(gòu),從而實現(xiàn)更靈活的組件復用,具名插槽允許父組件向子組件傳遞多個不同的?HTML?結(jié)構(gòu),在?React?中,我們沒有直接的插槽概念,但可以通過?props.children?和函數(shù)作為?props?來實現(xiàn)類似的功能
    2025-01-01
  • 詳解React Hooks是如何工作的

    詳解React Hooks是如何工作的

    React Hooks是在React 16.8版本新增的特性,在我看了React官網(wǎng)和一些博客對React Hook的講解后還是覺得沒有g(shù)et到本質(zhì)。本篇博客通過手動實現(xiàn)useState()來了解Hook的原理和本質(zhì)。閱讀此篇博客的前提是你要知道一些 React Hooks的基本用法和使用規(guī)則,不然會看得云里霧里。
    2021-05-05
  • React Native基礎(chǔ)入門之調(diào)試React Native應(yīng)用的一小步

    React Native基礎(chǔ)入門之調(diào)試React Native應(yīng)用的一小步

    這篇文章主要給大家介紹了關(guān)于React Native基礎(chǔ)入門之調(diào)試React Native應(yīng)用的相關(guān)資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-07-07

最新評論

陆河县| 陇西县| 葵青区| 海淀区| 嘉禾县| 千阳县| 芮城县| 页游| 富裕县| 新宁县| 阿拉善左旗| 武山县| 渭源县| 政和县| 印江| 长泰县| 无棣县| 汾阳市| 宜城市| 莫力| 琼中| 鸡泽县| 旌德县| 郓城县| 崇阳县| 宜章县| 独山县| 泰来县| 即墨市| 松原市| 巴楚县| 潜江市| 新建县| 剑川县| 莱芜市| 手游| 潞城市| 随州市| 灵川县| 克拉玛依市| 和政县|