轉載好文《深入理解react-native》
http://blog.ilibrary.me/2016/12/25/react-native-internal
------------------------------------------------------------------------------------
術語、簡寫約定
名詞/縮寫 | 解釋 |
---|---|
React Native | Facebook出的跨平台應用構建框架 |
ReactJS | Facebook出的Web UI JS框架,具有革新性的編程模式 |
React | 若無特殊說明,React就是ReactJS |
NodeJS | 基於JavaScript V8 Engine 的一個javascript runtime |
RN | React Native的縮寫 |
N2J | Native to JavaScript call |
J2N | JavaScript to Native call |
JavaScript | 我們常說的JavaScript腳本語言 |
JS | JavaScript |
JS Engine | JavaScript腳本解釋執行引擎 |
JavaScriptCore | iOS/Android 平台上默認的JS Engine, 來源於Webkit, 無特殊說明情況下面,本文所有解釋都會默認基於JavaScriptCore |
JSC | JavaScriptCore |
JSX | JavaScript的一個語言擴展,在JSX里面你可以用標記語言來描述React組件。不用JSX也可以寫ReactJS,但是有JSX會高效很多 |
Bridge | React Native里面實現JS和原生代碼互相調用的模塊,該詞指向兩個概念,一個是RCTBridge,找個用來管理Bridge的組件,第二個是所有自定義的用於RN的原生功能。所有原生功能想要在JS里面使用都需要有定制的bridge來支持,所以我們把這個定制的模塊(包括native和js端)也稱之為bridge |
React Native簡介
React Native是Facebook出品的一個革新性的跨平台UI框架,跨平台不是它最大的亮點,它背后的[React]才應該是它的神奇說在,也是它革新所在。我在另外一篇博客React Native系列(3) - props和state中有詳細的分析。如果非要用一兩句來總結它的偉大,那就是給把web開發中的無狀態開發模式通過React實現了。 那些數不清的狀態組合才是桌面應用和手機應用復雜的源頭。
本文目的是通過源碼分析,詳細解釋React Native框架的內部結構及運行原理。文章會比較長,組織上會盡可能由淺入深來講。適合的讀者對象是對React Native開發有一定基礎的開發者。
分析的代碼是基於0.50.3, 基於iOS平台的實現,包括原生的代碼,js框架和打包器。從代碼打包,react native初始化,js加載到運行以及錯誤處理。安卓平台的Java代碼解讀不在本文覆蓋范圍。
ReactJS 簡介
ReactJS是一個非常具有革新性的web UI框架,非常簡單易用。它的virtual dom和data driven編程模式對現有的UI編程模式是一種顛覆, 極大簡化了UI應用復雜狀態的管理,非常值得大家去試一下。ReactJS配上Redux, 你會發現做復雜多狀態的應用居然可以如此簡單! React Native強大的原因就在於它是基於ReactJS的。相信讀這篇文章的人大部分以對ReactJS有一定的了解,這里就不多說。
JavaScriptCore
講React Native之前,了解JavaScriptCore會有幫助,也是必要的。React Native的核心驅動力就來自於JS Engine. 你寫的所有JS和JSX代碼都會被JS Engine來執行, 沒有JS Engine的參與,你是無法享受ReactJS給原生應用開發帶來的便利的。在iOS上,默認的就是JavaScriptCore, iOS 7之后的設備都支持. iOS 不允許用自己的JS Engine. JavaScriptCore來自於WebKit, 所以,安卓上默認也是用JavaScriptCore.
所以,你深入了解React Native的第一站應該是 JavaScriptCore.
nshipster.cn有一篇文章 對JavaScriptCore的使用有一些簡單的使用說明。讀完這篇文章你會發現,JavaScriptCore使用起來是非常簡單的,這得益於JavaScript的簡潔設計。所以,本文不會花精力講解JavaScriptCore。
相信有JavaScript和OC基礎的同學只需要花大概30秒就可以把上面的這篇文章讀完。
恭喜你! 當你讀完上面的那篇文章以后,你已經掌握了自己創造React Native框架的核心技術!一點也不誇張,JavaScriptCore在iOS平台上給React Native提供的接口也僅限於那幾個接口,你弄明白了JavaScriptCore那幾個接口, React Native 剩下的魔法秘密都可以順藤摸瓜來分析了。
本文接下來要講解的就是Facebook圍繞這幾個接口以及用一個React來顛覆整個native開發所做的精妙設計和封裝。你如果想自己做一個基於JS Engine做一個類似React Native的框架出來,建議研究JSPatch, 另外,還有一個外國朋友寫了一個博客講解用Edge JS引擎自己動手寫一個bridge. React Native的封裝非常龐大,涉及了很多的話題,直接與JS Engine相關的不多。
瀏覽器工作原理
瀏覽器通過Dom Render來渲染所有的元素.
瀏覽器有一整套的UI控件,樣式和功能都是按照html標准實現的。
瀏覽器能讀懂html和css。
html告訴瀏覽器繪制什么控件(html tag),css告訴瀏覽器每個類型的控件(html tag)具體長什么樣。
瀏覽器的主要作用就是通過解析html來形成dom樹,然后通過css來點綴和裝飾樹上的每一個節點。
UI的描述和呈現分離開了。
- html文本描述了頁面應該有哪些功能,css告訴瀏覽器該長什么樣。
- 瀏覽器引擎通過解析html和css,翻譯成一些列的預定義UI控件,
- 然后UI控件去調用操作系統繪圖指令去繪制圖像展現給用戶。
- Javascript可有可無,主要用於html里面一些用戶事件響應,DOM操作、異步網絡請求和一些簡單的計算。
在react native 里面,1和2是不變的,也是用html語言描述頁面有哪些功能,然后stylesheet告訴瀏覽器引擎每個控件應該長什么樣。並且和瀏覽器用的是同一個引擎。
在步驟3里面UI控件不再是瀏覽器內置的控件,而是react native自己實現的一套UI控件(兩套,android一套,ios一套),這個切換是在MessageQueque中進行的,並且還可以發現,他們tag也是不一樣的。
Javascript在react native里面非常重要,
- 它負責管理UI component的生命周期,管理Virtual DOM
- 所有業務邏輯都是用javascript來實現或者銜接
- 調用原生的代碼來操縱原生組件。
- Javascript本身是無繪圖能力的,都是通過給原生組件發指令來完成
React Native 架構
先上一副React Native 架構圖,這是我在內部培訓的時候畫的一副圖。
- 綠色的是我們應用開發的部分。我們寫的代碼基本上都是在這一層。
- 藍色代表公用的跨平台的代碼和工具引擎,一般我們不會動藍色部分的代碼。
- 黃色代碼平台相關的代碼,做定制化的時候會添加修改代碼。不跨平台,要針對平台寫不同的代碼。iOS寫OC, android寫java,web寫js. 每個bridge都有對應的js文件,js部分是可以共享的,寫一份就可以了。如果你想做三端融合,你就得理解這一個東西。如果你要自己定制原生控件,你就得寫bridge部分。
- 紅色部分是系統平台的東西。紅色上面有一個虛線,表示所有平台相關的東西都通過bridge隔離開來了。
- 大部分情況下我們只用寫綠色的部分,少部分情況下會寫黃色的部分。你如果對基礎架構和開源感興趣,你可以寫藍色部分,然后嘗試給那些大的開源項目提交代碼。紅色部分是獨立於React Native的,不討論。
React Native、React和JavascriptCore的關系
React Native最重要的三個概念應該就是React Native、React和JavascriptCore.
理解這三者的關系以后大家就可以自己去深入研究React Native了。
React是一個純JS庫,所有的React代碼和所有其它的js代碼都需要JS Engine來解釋執行。因為種種原因,瀏覽器里面的JS代碼是不允許調用自定義的原生代碼的,而React又是為瀏覽器JS開發的一套庫,所以,比較容易理解的事實是React是一個純JS庫,它封裝了一套Virtual Dom的概念,實現了數據驅動編程的模式,為復雜的Web UI實現了一種無狀態管理的機制, 標准的HTML/CSS之外的事情,它無能為力。調用原生控件,驅動聲卡顯卡,讀寫磁盤文件,自定義網絡庫等等,這是JS/React無能為力的。
你可以簡單理解為React是一個純JS 函數, 它接受特定格式的字符串數據,輸出計算好的字符串數據。
JS Engine負責調用並解析運行這個函數。
React Native呢? 它比較復雜。復雜在哪里?前面我們說了React 是純JS庫,意味着React只能運行JS代碼,通過JS Engine提供的接口(Html Tag)繪制html支持的那些元素,驅動有限的聲卡顯卡。簡單點說, React只能做瀏覽器允許它做的事情, 不能調用原生接口, 很多的事情也只能干瞪眼。
React Native它可不一樣。
第一點,驅動關系不一樣。前面我們說的是, JS Engine來解析執行React腳本, 所以,React由瀏覽器(最終還是JS Engine)來驅動. 到了React Native這里,RN的原生代碼(Timer和用戶事件)驅動JS Engine, 然后JS Engine解析執行React或者相關的JS代碼,然后把計算好的結果返回給Native code. 然后, Native code 根據JS計算出來的結果驅動設備上所有能驅動的硬件。重點,所有的硬件。也就是說,在RN這里,JS代碼已經擺脫JS Engine(瀏覽器)的限制,可以調用所有原生接口啦!
第二點, 它利用React的Virtual Dom和數據驅動編程概念,簡化了我們原生應用的開發, 同時,它不由瀏覽器去繪制,只計算出繪制指令,最終的繪制還是由原生控件去負責,保證了原生的用戶體驗。
React組件結構(圖文)
React Native組件結構(圖文)
驅動硬件的能力決定能一個軟件能做多大的事情,有多大的主控性。研究過操作系統底層東西或者匯編的同學明白,我們大部分時候寫的代碼是受限的代碼,很多特權指令我們是沒法使用的,很多設備我們是不允許直接驅動的。我們現在的編程里面幾乎已經沒有人提中斷了,沒有中斷,硬件的操作幾乎會成為一場災難.
在一定程度上,React Native和NodeJS有異曲同工之妙。它們都是通過擴展JavaScript Engine, 使它具備強大的本地資源和原生接口調用能力,然后結合JavaScript豐富的庫和社區和及其穩定的跨平台能力,把javascript的魔力在瀏覽器之外的地方充分發揮出來。
JavaScriptCore + ReactJS + Bridges 就成了React Native。
- JavaScriptCore負責JS代碼解釋執行
- ReactJS負責描述和管理VirtualDom,指揮原生組件進行繪制和更新,同時很多計算邏輯也在js里面進行。ReactJS自身是不直接繪制UI的,UI繪制是非常耗時的操作,原生組件最擅長這事情。
- Bridges用來翻譯ReactJS的繪制指令給原生組件進行繪制,同時把原生組件接收到的用戶事件反饋給ReactJS。
- 要在不同的平台實現不同的效果就可以通過定制Bridges來實現。
#深入 Bridge 前面有提到, RN厲害在於它能打通JS和Native Code, 讓JS能夠調用豐富的原生接口,充分發揮硬件的能力, 實現非常復雜的效果,同時能保證效率和跨平台性。
打通RN任督二脈的關鍵組件就是Bridge. 在RN中如果沒有Bridge, JS還是那個JS,只能調用JS Engine提供的有限接口,繪制標准html提供的那些效果,那些攝像頭,指紋,3D加速,聲卡, 視頻播放定制等等,JS都只能流流口水,原生的、平台相關的、設備相關的效果做不了, 除非對瀏覽器進行定制。
Bridge的作用就是給RN內嵌的JS Engine提供原生接口的擴展供JS調用。所有的本地存儲、圖片資源訪問、圖形圖像繪制、3D加速、網絡訪問、震動效果、NFC、原生控件繪制、地圖、定位、通知等都是通過Bridge封裝成JS接口以后注入JS Engine供JS調用。理論上,任何原生代碼能實現的效果都可以通過Bridge封裝成JS可以調用的組件和方法, 以JS模塊的形式提供給RN使用。
每一個支持RN的原生功能必須同時有一個原生模塊和一個JS模塊,JS模塊是原生模塊的封裝,方便Javascript調用其接口。Bridge會負責管理原生模塊和對應JS模塊之間的溝通, 通過Bridge, JS代碼能夠驅動所有原生接口,實現各種原生酷炫的效果。
RN中JS和Native分隔非常清晰,JS不會直接引用Native層的對象實例,Native也不會直接引用JS層的對象實例(所有Native和JS互掉都是通過Bridge層會幾個最基礎的方法銜接的)。
Bridge 原生代碼負責管理原生模塊並生成對應的JS模塊信息供JS代碼調用。每個功能JS層的封裝主要是針對ReactJS做適配,讓原生模塊的功能能夠更加容易被用ReactJS調用。MessageQueue.js是Bridge在JS層的代理,所有JS2N和N2JS的調用都會經過MessageQueue.js來轉發。JS和Native之間不存在任何指針傳遞,所有參數都是字符串傳遞。所有的instance都會被在JS和Native兩邊分別編號,然后做一個映射,然后那個數字/字符串編號會做為一個查找依據來定位跨界對象。
Bridge各模塊簡介
本節介紹以下模塊
- RCTRootView,
- RCTRootContentView,
- RCTBridge,
- RCTBatchedBridge,
- RCTJavaScriptLoader,
- RCTContextExecutor,
- RCTModuleData,
- RCTModuleMethod,
RCTRootView
RCTRootView是React Native加載的地方,是萬物之源。從這里開始,我們有了JS Engine, JS代碼被加載進來,對應的原生模塊也被加載進來,然后js loop開始運行。 js loop的驅動來源是Timer和Event Loop(用戶事件). js loop跑起來以后應用就可以持續不停地跑下去了。
如果你要通過調試來理解RN底層原理,你也應該是從RCTRootView着手,順藤摸瓜。
每個項目的AppDelegate.m
的- (BOOL)application:didFinishLaunchingWithOptions:
里面都可以看到RCTRootView的初始化代碼,RCTRootView初始化完成以后,整個React Native運行環境就已經初始化好了,JS代碼也加載完畢,所有React的繪制都會有這個RCTRootView來管理。
RCTRootView做的事情如下:
- 創建並且持有RCTBridge
- 加載JS Bundle並且初始化JS運行環境.
- 初始化JS運行環境的時候在App里面顯示loadingView, 注意不是屏幕頂部的那個下拉懸浮進度提示條. RN第一次加載之后每次啟動非常快,很少能意識到這個加載過程了。loadingView默認情況下為空, 也就是默認是沒有效果的。loadingView可以被自定義,直接覆蓋RCTRootView.loadingView就可以了.開發模式下RN app第一次啟動因為需要完整打包整個js所以可以很明顯看到加載的過程,加載第一次以后就看不到很明顯的加載過程了,可以執行下面的命令來觸發重新打包整個js來觀察loadingView的效果 ` watchman watch-del-all && rm -rf node_modules/ && yarn install && yarn start – –reset-cache`, 然后殺掉app重啟你就會看到一個很明顯的進度提示.
- JS運行環境准備好以后把加載視圖用RCTRootContentView替換加載視圖.
- 所有准備工作就緒以后調用
AppRegistry.runApplication
正式啟動RN JS代碼,從Root Component()開始UI繪制。
一個App可以有多個RCTRootView, 初始化的時候需要手動傳輸Bridge
做為參數,全局可以有多個RCTRootView, 但是只能有一個Bridge
.
如果你做過React Native和原生代碼混編,你會發現混編就是把AppDelegate
里面那段初始化RCTRootView
的代碼移動到需要混編的地方,然后把RCTRootView
做為一個普通的subview來加載到原生的view里面去,非常簡單。不過這地方也要注意處理好單Bridge
實例的問題,同時,混編里面要注意RCTRootView
如果銷毀過早可能會引發JS回調奔潰的問題。
RCTRootContentView
RCTRootContentView reactTag在默認情況下為1. 在Xcode view Hierarchy debugger 下可以看到,最頂層為RCTRootView, 里面嵌套的是RCTRootContentView
, 從RCTRootContentView開始,每個View都有一個reactTag.
RCTRootView繼承自UIView, RCTRootView主要負責初始化JS Environment和React代碼,然后管理整個運行環境的生命周期。 RCTRootContentView繼承自RCTView, RCTView繼承自UIView, RCTView封裝了React Component Node更新和渲染的邏輯, RCTRootContentView會管理所有react ui components. RCTRootContentView同時負責處理所有touch事件.
RCTBridge
這是一個加載和初始化專用類,用於前期JS的初始化和原生代碼的加載。
- 負責加載各個Bridge模塊供JS調用
- 找到並注冊所有實現了
RCTBridgeModule
protocol的類, 供JS后期使用. - 創建和持有 RCTBatchedBridge
RCTBatchedBridge
如果RCTBridge是總裁, 那么RCTBatchedBridge就是副總裁。前者負責發號施令,后者負責實施落地。
- 負責Native和JS之間的相互調用(消息通信)
- 持有JSExecutor
- 實例化所有在RCTBridge里面注冊了的native node_modules
- 創建JS運行環境, 注入native hooks 和modules, 執行 JS bundle script
- 管理JS run loop, 批量把所有JS到native的調用翻譯成native invocations
- 批量管理原生代碼到JS的調用,把這些調用翻譯成JS消息發送給JS executor
RCTJavaScriptLoader
這是實現遠程代碼加載的核心。熱更新,開發環境代碼加載,靜態jsbundle加載都離不開這個工具。
- 從指定的地方(bundle, http server)加載 script bundle
- 把加載完成的腳本用string的形式返回
- 處理所有獲取代碼、打包代碼時遇到的錯誤
RCTContextExecutor
封裝了基礎的JS和原生代碼互掉和管理邏輯,是JS引擎切換的基礎。通過不同的RCTCOntextExecutor來適配不同的JS Engine,讓我們的React JS可以在iOS、Android、chrome甚至是自定義的js engine里面執行。這也是為何我們能在chrome里面直接調試js代碼的原因。
- 管理和執行所有N2J調用
RCTModuleData
加載和管理所有和JS有交互的原生代碼。把需要和JS交互的代碼按照一定的規則自動封裝成JS模塊。
- 收集所有橋接模塊的信息,供注入到JS運行環境
RCTModuleMethod
記錄所有原生代碼的導出函數地址(JS里面是不能直接持有原生對象的),同時生成對應的字符串映射到該函數地址。JS調用原生函數的時候會通過message的形式調用過來。
- 記錄所有的原生代碼的函數地址,並且生成對應的字符串映射到該地址
- 記錄所有的block的地址並且映射到唯一的一個id
- 翻譯所有J2N call,然后執行對應的native方法。
- 如果是原生方法的調用則直接通過方法名調用,
MessageQueue
會幫忙把Method翻譯成MethodID, 然后轉發消息給原生代碼,傳遞函數簽名和參數給原生MessageQueue
, 最終給RCTModuleMethod解析調用最終的方法 - 如果JS調用的是一個回調block,
MessageQueue
會把回調對象轉化成一個一次性的block id, 然后傳遞給RCTModuleMethod, 最終由RCTModuleMethod解析調用。基本上和方法調用一樣,只不過生命周期會不一樣,block是動態生成的,要及時銷毀,要不然會導致內存泄漏。
- 如果是原生方法的調用則直接通過方法名調用,
注:
實際上是不存在原生MessageQueue對象模塊的,JS的MessageQueue對應到原生層就是RCTModuleData & RCTModuleMethod的組合, MessageQueue的到原生層的調用先經過RCTModuleData和RCTModuleMethod翻譯成原生代碼調用,然后執行.
BridgeFactory
JSCExecutor
MessageQueue
這是核心中的核心。整個react native對瀏覽器內核是未做任何定制的,完全依賴瀏覽器內核的標准接口在運作。它怎么實現UI的完全定制的呢?它實際上未使用瀏覽器內核的任何UI繪制功能,注意是未使用UI繪制功能。它利用javascript引擎強大的DOM操作管理能力來管理所有UI節點,每次刷新前把所有節點信息更新完畢以后再給yoga做排版,然后再調用原生組件來繪制。javascript是整個系統的核心語言。
我們可以把瀏覽器看成一個盒子,javascript引擎是盒子里面的總管,DOM是javascript引擎內置的,javascript和javascript引擎也是無縫鏈接的。react native是怎么跳出這個盒子去調用外部原生組件來繪制UI的呢?秘密就在MessageQueue。
javascript引擎對原生代碼的調用都是通過一套固定的接口來實現,這套接口的主要作用就是記錄原生接口的地址和對應的javascript的函數名稱,然后在javascript調用該函數的時候把調用轉發給原生接口
React Native 初始化
React Native的初始化從RootView開始,默認在AppDelegate.m:- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
里面會有RootViewd的初始化邏輯,調試的時候可以從這里入手。
React Native的初始化分為幾個步驟:
- 原生代碼加載
- JS Engine初始化(生成一個空的JS引擎)
- JS基礎設施初始化. 主要是require等基本模塊的加載並替換JS默認的實現。自定義require, Warning window, Alert window, fetch等都是在這里進行的。基礎設施初始化好以后就可以開始加載js代碼了。
- 遍歷加載所有要導出給JS用的原生模塊和方法, 生成對應的JS模塊信息,打包成json的格式給JS Engine, 准確地說是給MessageQueue. 這里需要提一下的是
- 這里的導出是沒有對象的,只有方法和模塊。JS不是一個標准的面向對象語言,剛從Java轉JavaScript的同學都會在面向對象這個概念上栽跟頭,這里特別提醒一下。
原生代碼初始化
這里討論的主要是RN相關的原生代碼和用戶自定義的RN模塊的原生代碼的加載和初始化。原生代碼初始化主要分兩步:
- 靜態加載。iOS沒有動態加載原生代碼的接口,所有的代碼都在編譯的初期就已經編譯為靜態代碼並且鏈接好,程序啟動的時候所有的原生代碼都會加載好。這是原生代碼的靜態加載,iOS里面沒有動態加載原生代碼的概念,這也是為何沒有靜態代碼熱更新的原因。
- RN模塊解析和注入JS。這是加載的第二步。在RootView初始化的時候會遍歷所有被標記為
RCTModule
的原生模塊,生成一個json格式的模塊信息,里面包含模塊名稱和方法名稱,然后注入到JS Engine, 由MessageQueue記錄下來。原生代碼在生成json模塊信息的時候同時會在原生代碼這邊維護一個名稱字典,用來把模塊和方法的名稱映射到原生代碼的地址上去,用於JS調用原生代碼的翻譯。
接下來我們就一步一步詳細講解原生代碼的初始化。
Javascript環境初始化
RN的初始化是從RCRootView開始的,所有的繪制都會在這個RootView里面進行(Alert除外).
RootView做的第一件事情就是初始化一個空的JS Engine。 這個空的JS Engine里面包含一些最基礎的模塊和方法(fetch, require, alert等), 沒有UI繪制模塊。 RN的工作就是替換這些基礎的模塊和方法,然后把RN的UI繪制模塊加載並注入到JS Engine.
JS Engine不直接管理UI的繪制。
- 所有的繪制由原生控制的UI事件和Timer觸發
- 影響界面刷新的事件發生以后一部分直接由原生控件消化掉,直接更新原生控件。剩下的部分會通過Bridge派發給MessageQueue,然后在JS層進行業務邏輯的計算,再由React來進行Virtual Dom的管理和更新。Virtual Dom再通過MessageQueue發送重繪指令給對應的原生組件進行UI更新。
NativeModules加載
在OC里面,所有NativeModules要加載進JS Engine都必須遵循一定的協議(protocol)。
模塊(OC里面的類)需要聲明為<RCTBridgeModule>
, 然后在類里面還必須調用宏RCT_EXPORT_MODULE()
用來定義一個接口告訴JS當前模塊叫什么名字。這個宏可以接受一個可選的參數,指定模塊名,不指定的情況下就取類名。
對應的JS模塊在初始化的時候會調用原生類的[xxx new]
方法.
模塊聲明為<RCTBridgeModule>
后只是告訴Native Modules這有一個原生模塊,是一個空的模塊。要導出任何方法給JS使用都必須手動用宏RCT_EXPORT_METHOD
來導出方法給JS用.
所有的原生模塊都會注冊到NativeModules這一個JS模塊下面去,你如果想要讓自己的模塊成為一個頂級模塊就必須再寫一個JS文件封裝一遍NativeModules里面的方法。
你如果想自己的方法導出就默認成為頂級方法,那么你需要一個手動去調用JSC的接口,這個在前面章節有講解。 不建議這樣做,因為這樣你會失去跨JS引擎的便利性。
你可以導出常量到JS里面去, 模塊初始化的時候會堅持用戶是否有實現constantsToExport
方法, 接受一個常量詞典。
- (NSDictionary *)constantsToExport { return @{ @"firstDayOfTheWeek": @"Monday" };// JS里面可以直接調用 ModuleName.firstDayOfTheWeek獲取這個常量 }
常量只會在初始化的時候調用一次,動態修改該方法的返回值無效
所有標記為RCT_EXPORT_MODULE
的模塊都會在程序啟動的時候自動注冊好這些模塊,主要是記錄模塊名和方法名。只是注冊,不一定會初始化。
Native Modules導出宏具體使用方法見官方文檔Native Modules
NativeModules懶加載
React Native的NativeModules是有延遲加載機制的。App初始化的時候
- React Native JS接口兼容(Polyfills)
- fetch替換
- CommonJS Require
- alert替換
- console.warning替換
- console.error替換
- 線程講解
- JS單線程和其背后的Native多線程
- JS的異步
- 三大線程
- 消息通信
- Native調用JS代碼
- 一般JS調用
- ReactNative里面的JS調用
- Native實現Promise
- 原生對象管理
- 模塊管理
- UIManager
- TagID
- JS調用Native代碼
- JS調用NativeModule代碼
- JS回調Native代碼
- 渲染
- ReactJS渲染機制
- ReactFiber/ReactStack
- ReactNative渲染
- 熱加載
- JS Reload
- JS代碼打包
- metro-bundler
- JSX
- babel
- Websocket
- YellowBox詳解
- RedBox詳解
- 調試菜單
- 自定義一個原生控件
- SplashScreen工作邏輯
三個線程
React Native有三個重要的線程:
- Shadow queue. 布局引擎(yoga)計算布局用的。
- Main thread. 主線程。就是操作系統的UI線程。無論是iOS還是android,一個進程都只有一個UI線程,我們常說的主線程. React Native所有UI繪制也是由同一個UI線程來維護。
- Javascript thread. javascript線程。 大家都知道javascript是單線程模型,event驅動的異步模型。React Native用了JS引擎,所以也必需有一個獨立的js 線程. 所有JS和原生代碼的交互都發生在這個線程里。死鎖,異常也最容易發生在這個線程.
可以看到Shadow queue是queue
而不是thread
, 在iOS里面queue
是thread
之上的一層抽象,GCD里面的一個概念,創建queue
的時候可以指定是並行的還是串行的。也就是說,一個queue
可能對應多個thread
。
As mentioned above, every module will have it’s own GCD Queue by default, unless it specifies the queue it wants to run on, by implementing the -methodQueue method or synthesizing the methodQueue property with a valid queue.
原生對象管理
待更新
消息機制(原生代碼調用)
待更新
有用的圖表
內部機制
Js調用時序,原文
Call Cyle
Executor Environment, 原文
Js to Java call, 出處bugly
消息循環, 出處