动态

详情 返回 返回

React-native的新架構 - 动态 详情

本文總結:

文章主要介紹了 React Native 的新架構,包括以下幾個方面的內容:📱✨

  • 如何抹平 iOS 和 Android 樣式差異,提升跨平台一致性;
  • 分析了舊架構中存在的問題,如通信瓶頸、啓動慢、維護複雜等;
  • 介紹了 JSI 中間層 帶來的變革,如:
  • 不再強依賴 JavaScriptCore 引擎;
  • 可直接在 JS 層調用 Native 方法,提升交互效率;
  • 講解了 RN 的渲染階段流程
  • 常用基礎庫(如 React Navigation)的配套使用;
  • 異常捕捉機制熱更新/包體更新的思路

🔍 關聯問題與亮點:

  • 新架構優勢是什麼?
  • 新架構通過引入 JSI、Fabric 和 TurboModules,解決了舊通信機制的性能瓶頸,提升了啓動速度和運行效率。Fabric 如何優化渲染?
  • Fabric 渲染引擎讓渲染流程更接近 React 生態,通過異步渲染、協調與 Commit 階段的優化,實現更流暢的 UI 體驗。
  • 🌈Turbo Modules 怎樣加速?
    TurboModules 通過 按需加載、延遲初始化 方式提升模塊加載性能,且與 JSI 結合使 JS 層調用 Native 層更加輕量、直接。

1、React-native的style

上一篇文章,我們應該已經對跨平台有了一定的概念,但這裏其實有一個問題並沒有解決,就是其實在ios和安卓上的樣式是有差異的,那麼我們的Rn就需要去抹平這種差異化,rn中採用的是css-in-js

我們在js中寫的style對象,將由單獨的一個線程去處理,也就是Shadow thread。在這個線程中由Yoga引擎(這也是facebook開發的)重新去計算app的佈局,這個引擎在計算了有關app的東西后,將結果又反饋至UI線程,最終呈現出來。
那麼一個完整的老版本的架構是這樣的:
圖片

然後我們現在把整個流程理一下: 假設我們現在有一段react的代碼

<View style={{width: 200, height: 200}}/> 

下一步就是js線程將其序列化

UIManager.createView([352,"RCTView",191,{width":200,"height":200}])

而此時這個task進入到了橋前的異步隊列中,它的目的地是ShadowThread,ShadowTread接收到這條信息後,先反序列化,形成Shadow tree,然後傳給Yoga,形成原生布局信息。

下一步又先序列化把信息傳給native線程,然後拿到後反序列化根據佈局信息去進行渲染和繪製。

大夥現在應該已經對一個rn的整體架構有了基本的瞭解,還記得上篇文章的問題嗎?橋的負載和異步會導致性能問題和不確定性。

  • 線程信息的傳遞因為要減小開銷每次都需要反覆序列化,但序列化又是一個消耗很大的事情。
  • 異步隊列的不確定性,你並不能保證一個事件的順序。
    *
    因此rn的新架構就是要去解決這些問題,也就是現在的中間層。

    2、React-native新架構

    關於新架構的內容很多,可能有些地方我自己也有理解不當的地方歡迎指正。

我們先講講最大的改動,就rn在新架構中直接把老的橋幹掉了,直接換成了一個新的中間層或者説通用層,也就是 JS Interface (JSI)。在這個通用層裏面有很多的新內容我們可以先看一下這個架構圖。
圖片
So,我們來看看有哪些變化,上面的圖中間部分,就是JSI。(解釋一下為啥這個圖是這樣的,因為就Turbo Modules我其實認為是Native Moudles的加強,而FabricRenderer的加強,他們是原本就存在的)。

1、JS-bundle不再強依賴JavaScriptCore引擎了。我們現在可以很方便用更好的引擎去替換了,性能更好了。比如Hermes

2、JSI讓我們可以直接在js層調用native的方法了。由HostObject C++ object實現,它直接存儲了native層方法和屬性的引用放在了一個全局對象上,然後我們js就可以直接調用java/oc的api
3、Turbo Modules的出現(上圖中的Native Moudles),在之前的架構中 JS 使用的所有 Native Modules(例如藍牙、地理位置、文件存儲等)都必須在應用程序打開之前進行初始化,這意味着即使用户不需要某些模塊,但是它仍然必須在啓動時進行初始化。

Turbo Modules 基本上是對這些舊的 Native 模塊的增強,正如在前面介紹的那樣,現在 JS 將能夠持有這些模塊的引用,所以 JS 代碼可以僅在需要時才加載對應模塊,這樣可以將顯着縮短 RN 應用的啓動時間。
4、Fabric也就是上圖中的renderer(以前shadow層是在native層實現的),一個新的UI渲染器,它就相當於在c++中,可以直接創建一個 ShadowTree,一個就是快,同時也減少了渲染元素的步驟。

可能大家沒懂,舉個例子:當 App 運行時,React 會執行你的代碼並在 JS 中創建一個 ReactElementTree ,基於這棵樹渲染器會在 C++ 中創建一個 ReactShadowTreeFabric 會使用 Shadow Tree 來計算 UI 元素的位置,而一旦 Layout 完成,Shadow Tree 就會被轉換為由 Native Elements 組成的 HostViewTree(例如:RN 裏的 會變成 Android 中的 ViewGroup 和 iOS 中的 UIView)。

5、 codegen其實就是一個靜態類型檢查器,CodeGen使用類型確定後的 JavaScript 來為Turbo Modules Fabric定義供他們使用的接口元素,並且它會在編譯時生成更多的native代碼,而非運行時。

3、RN的渲染

將 React 代碼渲染到宿主平台,我們稱為渲染流水線,可大致分為三個階段:

  • 渲染(Render):在 JavaScript 中,React 執行那些業務邏輯代碼創建 React 元素樹(React Element Trees)。然後在 C++ 中,用 React 元素樹創建 React 影子樹(React Shadow Tree)。
  • 提交(Commit):在 React 影子樹完全創建後,渲染器會觸發一次提交。這會將 React 元素樹和新創建的 React 影子樹的提升為“下一棵要掛載的樹”。 這個過程中也包括了佈局信息計算。
  • 掛載(Mount):React 影子樹有了佈局計算結果後,它會被轉化為一個宿主視圖樹(Host View Tree)。

    4、一些基本的庫

    Ok,上面都是框架的架構設計,我們先有一個大體的概念,那麼現在我們稍微走近實戰去了解一些必要的包,因為後面不會怎麼講。

React-native只內置了一些必要的包,但為了儘可能的減小包的大小,許多的包需要你自己去配置,例如:asyncStorage,這種sdk你需要一點點依賴相關的原生知識,但問題不大,一般都會有模版去教你,照着模版就行了(但也不一定,絕大數情況是)。那麼我們現在就看看一些常用的包。

3.1、React Navigation

這個應該幾乎是每個用rn的同學都該瞭解的東西了,原生appweb的路由是不同的,在app裏其實是沒有url這種概念,在原生裏要理解screen,也就是説控制用户所見屏幕。在老版本rn有一些原始導航組件去控制屏幕,但很複雜,所以就現在一般都會用到react-navigation這個庫。

我直接上實戰吧,

import * as React from "react";

import { NavigationContainer } from "@react-navigation/native";

import { createNativeStackNavigator } from "@react-navigation/native-stack";

import Home from "./Home";

import Settings from "./Settings";

const Stack = createNativeStackNavigator();

export default function App() {

return (

    <NavigationContainer>

        <Stack.Navigator>
          <Stack.Screen name="Home" component={Home} />

            <Stack.Screen name="Settings" component={Settings}/>

        </Stack.Navigator>

    </NavigationContainer>

);

}

createNativeStackNavigator是創建你的導航組件的一個方法,它返回一個對象,裏面有ScreenNavigator2個組件,他們用來配置導航


import React from "react";

import { View, Text, Button, StatusBar } from "react-native";

import styles from "./styles";
export default function Home({ navigation }) {

return (

<View style={styles.container}>

<StatusBar barStyle="dark-content" />

<Text>Home Screen</Text>

<Button

title="Settings"

onPress={() => navigation.navigate("Settings")}

/>

</View>

);

}

就看到home組件,當你按下的時候就跳轉到settings這個屏幕上去,更多的內容我們後面實戰的時候再講吧,只是做個簡單的演示。

3.2 RN組件庫

antd mobile估計國內我們基本用的都這個或者就是自己封裝的組件庫,推薦幾個其他的NativeBaseReact Native ElementUI KitternReact-native-paper

3.3 啓動頁

其實啓動頁就是你js線程啓動前展示的過度頁面,React-native-bootsplash。

3.4 Icon

react-native-vector-icons、 react-native-svg。

3.5 異常捕捉

通常,當我們開發一個web應用時,我們很好處理錯誤,因為它們不會超出JS的範圍。簡單的説我們前端就是web的王(掌控力),我們可以很容易地看到原因,並在DevTools中打開日誌。

Rn因為除了環境的JS之外,我們還有native組件,這也可能導致app執行中的錯誤。因此,當發生錯誤時,我們的應用程序將關閉立即我們很難弄清楚原因,因此React-native-exception-handler也正是解決這個問題的包。

就像這樣:


import { setJSExceptionHandler, setNativeExceptionHandler } 
from "react-native-exception-handler";
setJSExceptionHandler((error, isFatal) => {

});

const exceptionhandler = (exceptionString) => {

};

setNativeExceptionHandler(

exceptionhandler,

forceAppQuit,

executeDefaultHandler

);

3.6 包更新

其實如果是ios我們要更新應用上傳到商店,有這麼個技術OAT可以替換js包,就可以看看微軟的Codepush

4.結束
[rn中文文檔地址],就這2篇文章都是在理一些基礎理論的東西,對於一些組件api大夥可以看看文檔。歡迎加入羣聊,我們一起討論一些更有趣的技術、商業、閒聊。
圖片

user avatar jzxstudio 头像 wxp686 头像 xiaoxxuejishu 头像 zcf0508 头像 yayu 头像 wswx 头像 limingcan562 头像 meiyounvpengyoudeqikeng 头像 tinyang 头像 viewweiwu 头像 nine_59f82397ef519 头像 i_62f4ad94a52ca 头像
点赞 22 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.