React Native 新架構(gòu)
本文轉(zhuǎn)載自知乎專欄前端醬爆,作者章偉東,網(wǎng)易云音樂 前端工程師。
本文主要介紹FB團(tuán)隊正在重構(gòu)的ReactNative(下面稱RN)新架構(gòu),主要當(dāng)前架構(gòu),Bridge帶來的問題,新架構(gòu),JSI,F(xiàn)abric,TurboModules,CodenGen及LeanCore等概念。
當(dāng)前架構(gòu)

RN現(xiàn)在主要有3個線程
JS thread。JS代碼執(zhí)行線程,負(fù)責(zé)邏輯層面的處理。Metro(打包工具)將React源碼打包成一個單一JS文件(就是圖中JSBundle)。然后傳給JS引擎執(zhí)行,現(xiàn)在ios和android統(tǒng)一用的是JSC。
UI Thread(Main Thread/Native thread)。這個線程主要負(fù)責(zé)原生渲染(Native UI)和調(diào)用原生能力(Native Modules)比如藍(lán)牙等。
Shadow Thread。這個線程主要是創(chuàng)建Shadow Tree來模擬React結(jié)構(gòu)樹。Shadow Tree可以類似虛擬dom。RN使用Flexbox布局,但是原生是不支持,所以Yoga就是用來將Flexbox布局轉(zhuǎn)換為原生平臺的布局方式。
Bridge的問題
首先回顧一下當(dāng)前Bridge的運(yùn)行過程。
當(dāng)我們寫了類似下面的React源碼。
<View?style={{
??backgroundColor:?'pink',
??width:?200,
??height:?200}}
/>
JS thread會先對其序列化,形成下面一條消息
UIManager.createView([343,"RCTView",31,?{"backgroundColor":-16181,"width":200,"height":200}])
通過Bridge發(fā)到ShadowThread。Shadow Tread接收到這條信息后,先反序列化,形成Shadow tree,然后傳給Yoga,形成原生布局信息。
接著又通過Bridge傳給UI thread。
UI thread 拿到消息后,同樣先反序列化,然后根據(jù)所給布局信息,進(jìn)行繪制。
從上面過程可以看到三個線程的交互都是要通過Bridge,因此瓶頸也就在此。
Bridge三個特點:
異步。這些消息隊列是異步的,無法保證處理事件。
序列化。通過JSON格式來傳遞消息,每次都要經(jīng)歷序列化和反序列化,開銷很大。
批處理。對Native調(diào)用進(jìn)行排隊,批量處理。
異步設(shè)計的好處是不阻塞,這種設(shè)計在大部分情況下性能滿足需求,但是在某些情況下就會出問題,比如瀑布流滾動。
當(dāng)瀑布流向下滑動的時候,需要發(fā)請求給服務(wù)端拿數(shù)據(jù)進(jìn)行下一步渲染。
滾動事件發(fā)生在UI thread,然后通過Bridge發(fā)給JS thread。JS thread 監(jiān)聽到消息后發(fā)請求,服務(wù)端返回數(shù)據(jù),再通過Bridge返回給Native進(jìn)行渲染。由于都是異步,就會出現(xiàn)空白模塊,導(dǎo)致性能問題。
從上面可以看出,性能瓶頸主要是存在JS線程和Native有交互的情況,如果不存在交互,RN的性能良好。
因此,對于RN的優(yōu)化,主要集中在Bridge上,有下面3個原則:
JS和Native端不通信。最徹底的方式,消息不走Bridge。
JS和Native減少通信。在兩端無法避免的情況下,盡量通信減少次數(shù)。比如多個請求合并成一個。
較少JSON的大小。比如圖片轉(zhuǎn)為Base64會導(dǎo)致傳輸數(shù)據(jù)變大,用網(wǎng)絡(luò)圖片代替。
RN里面可以通過MessageQueue來監(jiān)聽Bridge通信,主要代碼如下
import?MessageQueue?from?'react-native/Libraries/BatchedBridge/MessageQueue.js';
const?spyFunction?=?(msg)?=>?{
??console.log(msg);
};
MessageQueue.spy(spyFunction);
下面是監(jiān)聽到的信息
新架構(gòu)
FB團(tuán)隊逐漸意識到了這些問題,同時也受到Flutter的壓力,在2018年提出了新架構(gòu)

主要有JSI、Fabric、TurboModules、CodeGen、LeanCode組成。
JSI
JSI是整個架構(gòu)的核心和基石,所有的一切都是建立在它上面。
JSI是Javascript Interface的縮寫,一個用C++寫成的輕量級框架,它作用就是通過JSI,JS對象可以直接獲得C++對象(Host Objects)引用,并調(diào)用對應(yīng)方法。
另外JSI與React無關(guān),可以用在任何JS 引擎(V8,Hermes)。
有了JSI,JS和Native就可以直接通信了,調(diào)用過程如下:
JS->JSI->C++->ObjectC/Java
自此三個線程通信再也不需要通過Bridge,可以直接知道對方的存在,讓同步通信成為現(xiàn)實。具體的用法可以看 官方例子。
另外一個好處就是有了JSI,JS引擎不再局限于JSC,可以自由的替換為V8,Hermes,進(jìn)一步提高JS解析執(zhí)行的速度。
Fabric
Fabric是整個架構(gòu)中的新UI層,包括了新架構(gòu)圖中的renderer和shadow thread。
下圖是舊的通信模型。
三個線程通過Bridge異步通信,數(shù)據(jù)需要拷貝多份。
有了JSI以后,JS可以直接掉調(diào)用其他線程,實現(xiàn)同步通信機(jī)制。另外數(shù)據(jù)可以直接引用,不需要拷貝,于是就變成了下面新的通信模式.
除了同步能力,直接引用,另外一個好處是Fabric現(xiàn)在支持渲染優(yōu)先級比如React的Concurrent和Suspense模式
下面兩張圖是從啟動到渲染階段,加入Fabric前后的變化。
改造為Fabric之后
TurboModules
TurboModules主要和原生應(yīng)用能力相關(guān),對應(yīng)新架構(gòu)圖上的Native Modules,這部分的優(yōu)化是:
通過JSI,可以讓JS直接調(diào)用Native模塊,實現(xiàn)一些同步操作。比如調(diào)用攝像頭能力。
Native模塊懶加載。之前RN框架啟動的時候會加載所有Native模塊,導(dǎo)致啟動慢,時間久。現(xiàn)在有了TurboModules后,可以實現(xiàn)按需加載,減少啟動時間,提高性能。
CodeGen
通過CodeGen,自動將Flow或者Ts等有靜態(tài)類型的JS代碼翻譯成Fabric和TurboModules使用的原生代碼。
Lean Core
這部分主要是包的瘦身,以前所有的包都放在RN核心工程里面。現(xiàn)在RN核心只保留必要的包,其他都移到react-native-community 或者拆出單獨的組件,比如Webview和AsyncStore。
當(dāng)前進(jìn)度
JSI已經(jīng)跟隨RN0.59(JSIExecuter.cpp)發(fā)布,但是任然使用Bridge來通信
Fabric和TurboModules還在開發(fā),LeanCore已經(jīng)完成
現(xiàn)在可以使用C++跨平臺模塊。
對JS會實現(xiàn)向下兼容,對Native Modules不會兼容。
具體的進(jìn)度可以參考Fabric進(jìn)度討論和 TurboModules進(jìn)度討論和JSI進(jìn)度討論和CodeGen進(jìn)度討論,以及React官方源碼
目前RN的新架構(gòu)正在緊張的重構(gòu)中,比預(yù)定的時間表晚了一點,比較期待新框架的發(fā)布和表現(xiàn)。
參考資料
react-native-fabric-why-am-i-so-excited
How React Native constructs app layouts
React Native — A Bridge To Project Fabric
Chen Feldman - React Native - Under the Bridge
點個『在看』支持下?





