【譯】React Native — 橋接Bridge 到 Fabric項目(RN新架構)

做者:Chen Feldmanhtml

發佈日期:2019.07.30前端

原文連接:medium.com/swlh/react-…react

翻譯中有部分是意譯。ios

最近幾年,React Native已經成爲最流行的移動端App開發框架之一。解決了不少開發者常常思考的問題:應該使用原生開發客戶端app,仍是使用web開發Hybird app。git

使用RN開發須要web方面的知識和React經驗,由於它基本是基於React框架的。須要比直接使用Cordova這樣庫更難一些,可是比寫Swift和Kotlin 這兩個原生客戶端的代碼要輕鬆不少。RN能夠經過寫純js代碼得到和原生相似的性能體驗。github

第一部分:RN的Bridge(橋接)是什麼黑魔法?

在開始以前,咱們先介紹一下,相比於其餘方案,React Native的定位是什麼。web

img
在上圖中,選取了一些常見的App開發方式。

從左到右,能夠分紅3個陣營:objective-c

  1. 原生Native。這意味着你想去使用原生語言開發App,好比ios的Swift或Android的Kotlin。確定會得到最好的性能,而且能夠充分利用設備硬件和原生API。可是,這須要學習兩種不一樣的編程語言,維護兩套代碼,而且可能有雙倍的bug,甚至須要有兩個不一樣的開發團隊。編程

  2. Hybrid。若是你是web開發者或者擁有一個已經熟悉JS、HTML、CSS和某些前端庫的Web團隊,你能夠選擇這種方案,並使用Cordova/Ionic 經過一些步驟使你的網頁變成移動App。這樣咱們只須要學習一個技術棧,可是,在性能,硬件和API的使用上會有限制。react-native

  3. 類原生(其實也算是一種Hybrid)。React Native就屬於這類,理論上開發者只須要懂得Web開發知識就能夠了。可是學習曲線會比Hybrid要高一些。開發者須要學習怎麼使用React Native的庫。在某些場景下,可能還須要使用XCode或Android Studio打開項目。可是寫的代碼能夠適用於iOS和Android兩個平臺,開發上的限制也會比Hybrid要小一些。性能表現會更像原生開發,並且能夠更容易地使用一些原生的API。

如今咱們理解了RN的定位,讓咱們對比一下RN和Hybrid的渲染狀況:

img

從上圖能夠看出,使用Cordova的App是放在webView裏的。這更像是一個在App裏的瀏覽器。用web的概念說,這更像一個web app裏的iFrame。

左邊展現了React Native是怎麼組成的,在RN中的每一個組件,好比:Text、Button、Image等都有一個相對應的原生組件。因此和不少新接觸RN開發者想的不同,RN並無編譯成原生代碼,它作了個JS組件和原生組件的映射。

當咱們這樣在Render函數裏寫了個RN組件:

img

背後部分預先寫好的原生組件會是這樣的:

img

若是咱們用iOS來舉例,這些是RN裏面的原生組件代碼:

img

總的來講,RN團隊已經爲咱們建立和映射了全部的原生組件。咱們須要去作的只是去基於RN組件和庫寫JavaScript代碼。其餘的部分對通常開發者來講都是不可見的,像黑魔法同樣。

因此,看上去咱們只用寫js代碼就能夠了。讓咱們一步步來解釋一下這個架構是怎樣的。從Bridge開始是最合適的:

RN能夠分紅JS 和 Native兩部分,這兩部分都有本身的線程

線程通訊是經過Bridge通訊,傳輸的是JSON信息,其中包含了module id,method id和一些須要的數據。這兩邊並不能直接地相互感知,也不能共享相同的內存。

img

這有點像不一樣服務器之間通訊,若是你有用不一樣語言寫的後臺服務,你將怎麼在他們之間通訊呢?

不少人認爲隊列是一個很好的解決方案。你發送JSON/XML隊列消息,這個消息聽從相應的協議,而且每一個服務都知道怎麼去讀取和解析成對應的數據和行爲。這個隊列就很像RN裏面的Bridge。

img

這裏有個例子展現通訊是怎樣進行的,信息被髮送到Bridge。從建立新的View和樣式並在屏幕上顯示,到在移動端設置子組件和進行一些操做:

img

若是想要在console裏看到Bridge的消息,只須要把下面這個代碼片斷放到index. < platform > .js裏就能夠了

img

如今咱們知道了RN是怎麼通訊的,而且開始揭曉其中的「黑魔法」。

  • js代碼和objective-c之間的通訊。RN在ios上使用的是內置的JSCore,在Android須要格外編譯一個Js引擎
  • JS引擎知道如何去更加高效地把JS轉換成機器語言。

這也是爲何Android App須要更多的時間去加載,由於須要加載JSCore到項目裏。不過在0.60.2以後,RN可使用Hermes,這對於RN來講是更理想的JS引擎。

第二部分:Bridge是怎麼工做的?

在這部分,咱們將經過解析從點擊App圖標到App打開這個流程和其中的一些相關細節。

img

爲了理解RN是怎麼在背後建立View的,咱們先須要解釋一些基礎概念:

  1. UIManager:在Native側,是在iOS/Android裏主要運行的線程。只有它有權限能夠修改客戶端UI。
  2. JS Thread:運行打包好的main.bundle.js文件,這個文件包含了RN的全部業務邏輯、行爲和組件。
  3. Shadow Node/Tree:在Native層的一個組件樹,能夠幫助監聽App內的UI變化,有點像ReactJS裏的虛擬Dom和Dom之間的關係。
  4. Yoga:用來計算layout。是Facebook寫的一個C引擎,用來把基於Flexbox的佈局轉換到Native的佈局系統。

理解了上面的一些基礎概念,讓咱們來看下打開App時,每一步發生了什麼:

  1. 用戶點擊App的圖標

  2. UIManager線程:加載全部的Native庫和Native組件好比 Text、Button、Image等

  3. 告訴Js線程,Native部分準備好了,Js側開始加載main.bundle.js,這裏麪包含了全部的js和react邏輯以及組件。

  4. Js側經過Bridge發送一條JSON消息到Native側,告訴Native怎麼建立UI。值得一提的是:全部通過Bridge的通訊都是異步的,而且是打包發送的。這是爲了不阻塞UI,舉個栗子:

img

  1. Shadow線程最早拿到消息,而後建立UI樹

  2. 而後,它使用Yoga佈局引擎去獲取全部基於flex樣式的佈局,而且轉化成Native的佈局,寬、高、間距等。。

  3. 以後UIManager執行一些操做而且像這樣在屏幕上展現UI:

    img

這些就是啓動App是的主要步驟。

RN基於這個架構有如下優勢:

  • UI不會被阻塞:用戶感受到更加流暢
  • 不須要寫Native側的代碼:使用RN庫的話,不少代碼能夠只寫JavaScript的
  • 性能更加接近Native
  • 整個流程是完整的。開發者不用去控制而且徹底瞭解它

可是,有優勢確定也會有缺點。下面咱們介紹一下可以解決現有缺點的新架構:Fabric。

第三部分:Bridge的優缺點和Fabric架構

咱們已經討論了RN的當前架構,是時候說一下其中的缺陷。能夠看一下Facebook的React團隊負責人在她博客裏提到的。

當前架構的缺點是:

  • 有兩個不一樣的領域:JS和Native,他們彼此之間並不能真正互相感知,而且也不能共享相同的內存。
  • 它們之間的通訊是基於Bridge的異步通訊。可是這也意味着,並不能保證數據100%並及時地到達另外一側。
  • 傳輸大數據很是慢,由於內存不能共享,全部在js和native之間傳輸的數據都是一次新的複製。
  • 沒法同步更新UI。比方說有個FlatList,當我滑動的時候會加載大量的數據。在某些邊界場景,當有用戶界面交互,但數據尚未返回,屏幕可能會發生閃爍。
  • RN代碼倉庫太大了。致使庫更重,開源社區貢獻代碼或發佈修復也更慢。

不過不要誤會,Facebook本身也正在使用React Native開發各類app,服務百萬級的日活用戶。同時也有其餘有名的公司在生產環境中使用基於當前架構的RN,好比Wix、Bloomberg、Tesla、Zynga等等。

RN開發團隊正着手去解決上面提到的缺點。

這是以前的RN架構:

當前結構的主要部分

這是新的架構圖:

img

讓咱們來解釋一下這些新的概念:JSI,Fabric,Turbo Modules,和 CodeGen。

  1. JSI(將會替換Bridge) -- 爲了讓JS和Native可以互相感知。將再也不須要經過Bridge傳輸序列化JSON。將容許Native對象被導出成Js對象,反過來也能夠。兩側也會導出能夠被同步調用的API。實際上,架構的其餘部分都是基於這個之上的(Fabric,Turbo Modules等,這些下面會解釋)

  2. Fabric -- UIManager的新名稱,將負責Native端渲染。和當前的Bridge不一樣的是,它能夠經過JSI導出本身的Native函數,在JS層能夠直接使用這些函數引用,反過來,Native層也能夠直接調用JS層。這帶來更好更高效的性能和數據傳輸。

  3. Turbo Modules。記得上面的Native組件嗎?Text、Image、View,他們的新名字叫Turbo Modules。組件的做用是相同的,可是實現和行爲會不一樣。第一,他們是懶加載的(只有當App須要的時候加載),而如今是在啓動時所有加載。另外,他們也是經過JSI導出的,因此JS能夠拿到這些組件的引用,而且在React Natvie JS裏使用他們。尤爲會在啓動的時候帶來更好的性能表現。

  4. CodeGen -- 爲了讓JS側成爲兩端通訊時的惟一可信來源。它可讓開發者建立JS的靜態類,以便Native端(Fabric和Turbo Modules)能夠識別它們,而且避免每次都校驗數據 => 將會帶來更好的性能,而且減小傳輸數據出錯的可能性。

  5. Lean Core -- 是對React Native庫架構的變化。目的是減輕lib的負擔,並幫助社區更快地解決更多pull request。Facebook正在把庫的某些部分拆分出來,經過關注Github就能夠看出他們在這方面的行動了。好比這個:

    img

順便一提,這裏有個例子,你能夠在Chrome裏嘗試一下,這也是JSI如何工做和導出對象的靈感來源:

在Chrome裏打開開發者界面,而後輸入console.log,回車。你將看到native code。這說明console.log實際上是一個Native的函數。

在Chrome裏打開開發者界面,而後輸入console.log,回車。你將看到native code

接下來咱們再對比一下使用新架構,App啓動的流程是怎麼樣的。

  1. 用戶點擊App的圖標

  2. Fabric加載Native側(沒有Native組件)

  3. 而後通知JS線程Native側準備好了,JS側會加載全部的main.bundle.js,裏面包含了全部的js和react邏輯+組件

  4. JS經過一個Native函數的引用(JSI API導出的)調用到Fabric,同時Shadow Node建立一個和之前同樣的UI樹。

  5. Yogo執行佈局計算,把基於Flexbox的佈局轉化成終端的佈局。

  6. Fabric執行操做而且顯示UI==>

    img

爲了完成整個流程,咱們幾乎作了一樣的事情,可是沒有了Bridge,如今咱們能夠有更好的性能,咱們能夠用同步的方式進行操做,甚至能夠對UI上的同步操做進行優先級排序。啓動時間也將更快,App也將更小。

因此,咱們何時能夠上手使用這些東西?

能夠在Github上關注它們各自的更新狀況:

  1. JSI
  2. Fabric
  3. Turbo Modules
  4. Lean Core
  5. CodeGen

按理說,大多數的更改將逐步完成,並有但願在2019第四季度或2020第一季度發佈。

部分參考文檔,便於更深入地理解:

相關文章
相關標籤/搜索