長久以來,React Native 的效能問題一直為人詬病,為了解決這個問題,React Native 開發團隊針對舊有的架構進行了重構,並在 0.68 版本推出了新架構(New Architecture)。接下來,我會分享原來的架構是 Javascript 如何與 natvie 溝通,究竟面臨了什麼問題?讓 React Native 開發團隊決定重構。
要來了解現行架構是怎麼運作,先來認識幾個重要的概念:
就是指執行 Javascript 程式碼的環境。
Hermes:
是基於 React Native 打造的 JS Engine,在 React Native 0.70 版時被設為預設的 JS Engine。
JavaScriptCore:
Apple 開發的 JS Engine,較舊版本的 React Native 會使用這個引擎。
就是把資料轉換成 JSON 格式的過程,反之, JSON 反序列化就是把 JSON 格式轉成原始資料的過程。
是一種跨平台佈局引擎(Layout engine),主要是用來計算使用者畫面中 UI 元素的位置,藉此生成每一個 React shadow node 的位置與大小,ex:把 Flexbox 佈局轉換成原生平台的佈局樣式。
react 經過 Metro 打包編譯後產生 JS Bundle,JS Bundle 在 JavaScriptcore (JavaScript 引擎)的環境下執行,JavaScript 的程式碼與原生的程式碼透過 橋接(Bridge) 的方式溝通。
橋接(Bridge)是如何進行溝通呢? 原生模組(Native module) 透過 Bridge 封裝成 JavaScript 接口提供給 JavaScript 引擎調用 Native 的方法。
Native 與 JavaScript 之間溝通的方法是使用非同步的 JSON 序列化與反序列化方式去傳遞與轉換資料,也就是 JS thread 會傳送以 JSON 序列化的訊息並且以字串符發送到 Bridge,Bridge 會優化這個訊息並且傳遞到 UI Thread/ Native Thread,UI Thread/ Native Thread 收後解密這條訊息,然後根據這條訊息去執行原生的程式碼。
當應用程式啟動時,UI Manager 會下載所有的原生模組,React 會在 JavaScript Thread 執行產生 React Element Tree ,一旦渲染時就會透過 Bridge 發佈變更的指令到 Shadow Thread,創造 React Shadow Tree (似 React Virtual DOM 功能),顯示應用程序的 UI 的佈局和結構來比對 UI 需進行哪些更改,然後透過 yoga 引擎去計算 UI 元素在原生螢幕的位置和大小,一旦 Yoga 完成布局計算,React Native 的 UI Manager 就會將使用原生平台特定的 API 轉換成原生的 Host View Tree 並且通知 UI Thread 使用原生元素在用戶的螢幕上呈現變更後的 UI。
操作是非同步(asynchronous)的:
原生或 JavaScript 其中一端傳遞消息到 Bridge ,需要等另外一端處理這些消息,即使這個等待很沒必要。Bridge 就像是一條單線道的橋,當兩端都有車想要通過時,必須先等待其中一端過橋,另一端的車子才能過橋,曠時費力。
只能在單一執行緒(single-threaded)上進行:
JavaScript 只能在單一的 thread 上運行,所以會導致某些計算必須等待這個 thread 完成。
序列化造成效能消耗大:
由於透過 JSON 格式傳遞消息,每一次都經過序列化跟反序列化,造成效能的開銷很大。
以上的原因導致效能不佳,也連帶了影響使用者體驗。舉例來說,有一個很長的列表,當使用者在使用手勢快速向下滑動的時候,就很可能在剩下的資料回傳之前看到螢幕出現白屏。造成白屏的原因就在於:
當使用者操作速度一快,Bridge 就塞車了~畫面也就因此卡住了。
React Native 原理与实践
React Native: Understanding Threads.
React Native’s bridge, under the hood
React Native 渲染原理
Does React Native have a 'Virtual DOM'?