APP中RN頁面渲染流程-ReactNative源碼分析

在APP啓動後,RN框架開始啓動。等RN框架啓動後,就開始進行RN頁面渲染了。
RN頁面原生側頁面渲染的主要邏輯實現是在RCTUIManager和RCTShadowView完成的。
通過看UIMananger的源碼可以看到,UIMananger導出給JS端的API接口在對UI的操作上,基本都會同時對 View 和 ShadowView 進行操作。
以更新視圖爲例:


RCTUIManager的作用
RCTUIManager的主要作用是負責管理React Native應用程序的視圖的創建、更新和銷燬;將原生組件註冊到JS端;處理原生和React Native之間的通信;
具體如下:
1.視圖的創建、更新和銷燬。RCTUIManager負責創建並管理應用程序中的所有視圖,包括文本、圖像、按鈕等。當需要更新或銷燬視圖時,RCTUIManager會負責處理相應的操作。
2.組件的註冊。RCTUIManager負責註冊應用程序中的所有組件,並提供相應的方法以便在React Native應用程序中使用。
3.原生和React Native之間的通信。RCTUIManager通過橋接層(Bridge)與原生平臺進行通信,使得React Native應用程序可以在原生平臺上運行和呈現UI界面。當React Native應用程序需要與原生平臺進行交互時,RCTUIManager會負責處理相應的操作。
 
RCTShadowView的作用 
RCTShadowView的主要作用是在APP中創建一棵YaGoNode節點樹,用於記錄視圖的樣式、佈局、事件響應等信息,用於描述真實視圖的屬性和佈局,從而提高渲染性能和效率。,它和UIView的關係類似於前端的虛擬DOM樹和DOM樹,兩者是一一對應的關係。js對View的操作會先更新虛擬DOM,然後ReactNative在合適的時機批量更新到真實的View上。

RN頁面的創建
在創建自定義RNView供js使用時,一般在創建一個RNView時,都要創建一個對應的RNViewManager用於管理Native與js的通訊。
UIMananger的創建在RN框架啓動時,它在創建時會通過RCT_EXPORT_METHOD()宏將操作view的添加,修改,刪除,調整層級等方法注入給js,供js操作原生view
RN框架啓動完成後則會進行RN頁面渲染。
 
首先,js引擎執行rn代碼,將rn中的組件轉換成原生view展示到頁面上。
js通過執行create代碼,創建原生View

原生側createView方法的主要執行步驟爲:

RCT_EXPORT_METHOD(createView
: (nonnull NSNumber *)reactTag viewName
: (NSString *)viewName rootTag
: (nonnull NSNumber *)rootTag props
: (NSDictionary *)props)
1.根據模塊名viewName從RCTBridge保存的全局變量中找到對應的模塊信息
2.根據模塊信息創建shadowview虛擬dom,保存到shadowView全局容器中
3.根據模塊信息在主線程創建原生view,保存到view全局容器中
 
然後,執行setChildren:設置子視圖
執行setChildren:設置子視圖, 會將view添加到容器view的reactSubviews中(shadowView和UIView都是放到對應容器的reactSubviews屬性中)[container insertReactSubview:view atIndex:index++];

原生側setChildren方法的主要執行步驟爲:

RCT_EXPORT_METHOD(setChildren : (nonnull NSNumber *)containerTag reactTags : (NSArray<NSNumber *> *)reactTags)
1.設置shadowView子視圖,shadowView是設置到yoga樹的葉子節點中:YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
2.把設置view子視圖任務添加到任務隊列,[_pendingUIBlocks addObject:block];隊列中的任務並不會立刻執行,而是等到合適的時機再執行。而當這個任務執行後,子View也並沒有到真實的subviews中,而是放置到了reactSubviews關聯屬性中 objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 
_pendingUIBlocks隊列執行時機
在js執行期間,js引擎通過Bridge橋接,把涉及到UI操作的事件按順序封裝成UIBlock放到Native原生側的_pendingUIBlocks中,在等js代碼執行完成後,原生模塊會觸發一個UIManager.batchDidComplete事件,表示js批量任務執行完成,開始刷新uiPending隊列中的UI任務了。因此,在 JavaScript 執行完成前,RN 頁面的 UI 並不會立即刷新。
方法調用順序:batchDidComplete -> _layoutAndMount -> flushUIBlocksWithCompletion。
_pendingUIBlocks中的UIBlock執行後,最終會生成真實的原生view
- (void)didUpdateReactSubviews
{
  for (UIView *subview in self.reactSubviews) {
    [self addSubview:subview];
  }
}
 
 
RN頁面更新
當組件調用了setState屬性更新時,通過updateView:刷新視圖。
當出現插入、刪除、排序組件時,通過manageChildren:更新視圖。
updateView:刷新視圖
當在RN中通過setState更改屬性,js會對應生成一個新的虛擬DOM,通過diff算法,對應新舊DOM樹生成修改點,然後通過updateView事件,將屬性更新更新到原生側的shadowView和View的_UIPendingQueue中。
 
當出現插入、刪除、排序組件時,通過manageChildren:更新視圖
containerTag:表示容器組件的標識符,即將在其中管理子組件。
moveFromIndices和moveToIndices:表示要移動的子組件的原始位置和目標位置的索引。
addChildReactTags和addAtIndices:表示要添加的子組件的標識符和它們在父容器中的位置索引。
removeAtIndices:表示要從父容器中刪除的子組件的位置索引。
registry:表示React組件的註冊表,其中包含所有已註冊的組件及其實例。

 

 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章