原文位址:http://www.cocoachina.com/programmer/20170505/19189.html
前言
花了半個多月,把React Native源碼看了一遍,大概的實作邏輯全看明白了,希望對想了解React Native實作原理的同學有所幫助,其實隻要看懂文章的四幅圖就明白它的原理了。
如果喜歡我的文章,可以關注我微網誌:袁峥Seemygo,也可以來小碼哥,了解下我們的iOS教育訓練課程。後續還會更新更多内容,有任何問題,歡迎簡書留言袁峥Seemygo。。。
一、React Native背景
- 有沒有朋友想過一個問題,為什麼取名React Native?React是什麼,Native又是什麼?
React
- React 是由Facebook推出的一個JavaScript架構,主要用于前段開發。
- React 采用元件化方式簡化Web開發
DOM:每個HTML界面可以看做一個DOM
原生的web開發方式,HTML一個檔案,javaScript一個檔案,檔案分開,就會導緻修改起來比較麻煩。
可以把一組相關的HTML标簽和JavaScript單獨封裝到一個元件類中,便于複用,友善開發。
- React 可以高效的繪制界面
原生的Web,重新整理界面(DOM),需要把整個界面重新整理.
React隻會重新整理部分界面,不會整個界面重新整理。
因為React獨創了Virtual DOM機制。Virtual DOM是一個存在于記憶體中的JavaScript對象,它與DOM是一一對應的關系,當界面發送變化時,React會利用DOM Diff算法,把有變化的DOM進行重新整理.
- React是采用JSX文法,一種JS文法糖,友善快速開發。
Native
- 想要了解Native是什麼,需要了解下開發App有哪些開發模式,賣了一個館子,請繼續往下看。
二、常見的五種App開發模式
- 常見的開發模式有5種(Native App,Web App,Hybrid App,Weex,React Native)
Native App
- Native App:指使用原生API開發App,比如iOS用OC語言開發
- 優點:性能高
- 缺點:開發維護成本高,養一個原生開發工程師需要很多錢,最重要iOS版本更新也成問題。
Web App
- Web App:指使用Html開發的移動端網頁App,類似微信小程式,整個App都是網頁。
- 優點:使用者不需要安裝,不會占用手機記憶體
- 缺點:使用者體驗不好,不能離線,必須聯網
Hybrid App
- Hybrid App:混合開發模式,原生Api+Html共同開發,比如iOS,用html寫好界面,用UIWebView展示。
- 優點:界面複用性強,一個界面,iOS和安卓都可以使用
- 缺點:相對于原生,性能相對有所損害
Weex
- Weex:基于Vue(JS架構)的文法開發的App,底層會自動把JS代碼解析成對應平台(iOS,安卓)的原生API,本質還是原生API開發,隻不過表面是用Vue開發。
- 優點:可以做到一套代碼,跨平台執行,底層會自動判斷目前是哪個平台,轉換為對應平台的原生API代碼。
- 缺點:開源較晚,網際網路上相關資料還比較少,社群規模較小
React Native
- React Native:基于React開發的App
- 優點:跳過App Store稽核,遠端更新代碼,提高疊代頻率和效率,既有Native的體驗,又保留React的開發效率。
- 缺點:對于不熟悉前端開發的人員上手比較慢,不能真正意義上做到跨平台,使用後,對app體積增加。
- 相信大多數人了解完React Native,越來越困惑了,那不是跟Native沖突了嗎,Native是用原生Api開發,但是React Native又是用React開發。
- 要想徹底搞明白,需要了解React Native底層實作原理,又來了,想知道原理,繼續往下看
三、React Native原理
- React Native原理其實跟Weex差不多,底層也會把React轉換為原生API
- React Native和Weex差別在于跨平台上面,Weex隻要寫一套代碼,React Native需要iOS,安卓都寫,說明React Native底層解析原生API是分開實作的,iOS一套,安卓一套。
四、React Native如何把React轉化為原生API
- React Native會在一開始生成OC子產品表,然後把這個子產品表傳入JS中,JS參照子產品表,就能間接調用OC的代碼。
相當于買了一個機器人(OC),對應一份說明書(子產品表),使用者(JS)參照說明書去執行機器人的操作。
五、React Native是如何做到JS和OC互動
- iOS原生API有個JavaScriptCore架構,通過它就能實作JS和OC互動,想了解JavaScriptCore,請點選JavaScriptCore
1.首先寫好JSX代碼(React架構就是使用JSX文法)
2.把JSX代碼解析成javaScript代碼
3.OC讀取JS檔案
4.把javaScript代碼讀取出來,利用JavaScriptCore執行
5.javaScript代碼傳回一個數組,數組中會描述OC對象,OC對象的屬性,OC對象所需要執行的方法,這樣就能讓這個對象設定屬性,并且調用方法。

JS和OC互動.png
六、React Native啟動流程(iOS)
- 1.建立RCTRootView -> 設定視窗根控制器的View,把RN的View添加到視窗上顯示。
- 2.建立RCTBridge -> 橋接對象,管理JS和OC互動,做中轉左右。
- 3.建立RCTBatchedBridge -> 批量橋架對象,JS和OC互動具體實作都在這個類中。
- 4.執行[RCTBatchedBridge loadSource] -> 加載JS源碼
- 5.執行[RCTBatchedBridge initModulesWithDispatchGroup] -> 建立OC子產品表
- 6.執行[RCTJSCExecutor injectJSONText] -> 往JS中插入OC子產品表
- 7.執行完JS代碼,回調OC,調用OC中的元件
- 8.完成UI渲染
React Native啟動流程(iOS).png
七、React Native加載JS源碼流程(iOS)
- 1.[RCTJavaScriptLoader loadBundleAtURL] -> 加載遠端伺服器中JS代碼
- 2.attemptAsynchronousLoadOfBundleAtURL(C函數) -> 開啟異步加載JS代碼
- 3.[RCTBatchedBridge executeSourceCode:sourceCode] -> 讓批量交接對象執行源代碼
- [RCTJSCExecutor executeApplicationScript] -> 交給JS執行者(RCTJSCExecutor)執行源碼)
- 真正執行JS代碼的是RCTJSCExecutor對象
- 5.[postNotificationName:RCTJavaScriptDidLoadNotification] -> 發送JS代碼執行完成通知
- 6.RCTRootView監聽到RCTJavaScriptDidLoadNotification通知
- 7.建立RCTRootContentView
- 8.擷取RCTBridge中的RCTUIManager注冊RCTRootView,并且記錄RCTRootView,_viewRegistry
RCTUIManager:管理UI元件
_viewRegistry:儲存所有注冊的View
- 9.[RCTRootView runApplication:bridge] -> 通知JS運作App
- 10.[RCTBridge enqueueJSCall:@"AppRegistry"
1 2 3 | |
- 11.[RCTBatchedBridge _actuallyInvokeAndProcessModule:module method:method arguments:args queue:RCTJSThread] -> 通過批量橋架讓JS執行AppRegistry方法
- 12.[RCTJSCExecutor _executeJSCall:bridgeMethod arguments:@[module, method, args] unwrapResult:unwrapResult callback:onComplete] -> 讓JS執行者調用JS代碼
- 13.執行完JS代碼,就能擷取執行JS結果,是一個數組,OC需要做的事情都會儲存到這個數組中
- 14.[RCTBatchedBridge _processResponse:json error:error] -> 處理執行完JS代碼傳回的結果對象
- 15.[RCTBatchedBridge handleBuffer] -> 處理JS傳回的資料,JS會傳回的方法調用數組:按順序描述需要調用哪個對象的方法,一組調用包含(module,method,arguments)
- 16.[self callNativeModule:[moduleIDs[index] integerValue]
1 2 | |
注意:目前方法,在周遊數組中的代碼塊中執行,不隻是執行一次.
React Native加載JS源碼流程.png
八、React NativeUI控件渲染流程(iOS)
- 1.[RCTRootView runApplication:bridge] -> 通知JS運作App
- 2.[RCTBatchedBridge _processResponse:json error:error] -> 處理執行完JS代碼(runApplication)傳回的相應,包含需要添加多少子控件的資訊。
- 3.[RCTBatchedBridge batchDidComplete] -> 批量橋架對象調用批量處理完成方法
- 4.[RCTUIManager batchDidComplete] -> RCTUIManager調用批量處理完成的方法,就會開始去加載rootView的子控件。
- 5.[RCTUIManager createView:viewName:rootTag:props] -> 通過JS執行OC代碼,讓UI管理者建立子控件View
通過RCT_EXPORT_METHOD宏定義createView這個方法
1 2 3 4 | |
RCT_EXPORT_METHOD宏:會在JS中生成對應的OC方法,這樣JS就能直接調用
- 注意每建立一個UIView,就會建立一個RCTShadowView,與UIView一一對應
- RCTShadowView:儲存對應UIView的布局和子控件,管理UIView的加載
- 6.[RCTUIManager _layoutAndMount] -> 布局RCTRootView和增加子控件
- 7.[RCTUIManager setChildren:reactTags:] -> 給RCTRootView對應的RCTRootShadowView設定子控件
注意:此方法也是JS調用OC方法
- 8.[RCTRootShadowView insertReactSubview:view atIndex:index++] -> 周遊子控件數組,給RCTRootShadowView插入所有子控件
- 9.[RCTShadowView processUpdatedProperties:parentProperties:] -> 處理儲存在RCTShadowView中屬性,就會去布局RCTShadowView對應UIView的所有子控件
- 10.[RCTView didUpdateReactSubviews] -> 給原生View添加子控件
- 11.完成UI渲染
React Native (UI控件渲染流程).png
九、React Native事件處理流程(iOS)
- 1.在建立RCTRootContentView的時候,内部會建立RCTTouchHandler
RCTTouchHandler:繼承UIGestureRecognizer,也就是它就是一個手勢
它會作為RCTRootContentView的手勢,這樣點選RCTRootContentView,就會觸發RCTTouchHandler
RCTTouchHandler:内部實作了touchBegin等觸摸方法,用來處理觸摸事件
- 2.在建立RCTTouchHandler的時候,内部會建立RCTEventDispatcher
RCTEventDispatcher:用來把事件處理傳遞給JS的方法處理,也就是當UI界面産生事件,就會執行JS的代碼處理。
- 3.通過RCTRootContentView截獲點選事件
産生事件就會去觸犯RCTRootContentView中的RCTTouchHandler對象。
- 4.當産生事件的時候,會執行[RCTTouchHandler touchBegin]
- 5.RCTTouchHandler的touch方法,會執行[RCTTouchHandler _updateAndDispatchTouches:eventName:]
内部會建立RCTTouchEvent事件對象
- 6.[RCTEventDispatcher sendEvent:event] -> 讓事件分發對象調用發送事件對象
内部會把事件儲存到_eventQueue(事件隊列中)
- 7.[RCTEventDispatcher flushEventsQueue] -> 讓事件分發對象沖刷事件隊列,就是擷取事件隊列中所有事件執行
- 8.[RCTEventDispatcher dispatchEvent:event] -> 周遊事件隊列,一個一個分發事件
分發事件的本質:就是去執行JS的代碼,相應事件。
- 9.[RCTBatchedBridge enqueueJSCall:[[event class] moduleDotMethod] args:[event arguments]]; -> 讓橋架對象調用JS處理事件
本質:就是産生事件調用JS代碼
- 10.這樣就能完成把UI事件交給JS代碼相應
React Native (事件處理流程).png