基于React Native 0.83.1 新架構(gòu)下的拆包方案

React Native 0.83.1 新架構(gòu)(New Architecture) 下的拆包方案


React Native 0.83.1 新架構(gòu)拆包實戰(zhàn):基于 ReactHost 與單 Runtime 的動態(tài)加載方案

隨著 React Native 進入 0.80+ 時代,新架構(gòu)(Fabric & TurboModules)已成為主流。傳統(tǒng)的基于 ReactInstanceManager 和 Bridge 的拆包方式已逐步過時。本文將介紹在 RN 0.83.1 版本下,如何利用 ReactHost、JSIHermes Runtime 實現(xiàn)高性能的模塊化拆包與動態(tài)加載。


一、 為什么舊的拆包方案失效了?

在新架構(gòu)中,底層通信機制從傳統(tǒng)的 JSON Bridge 轉(zhuǎn)向了基于 C++ 的 JSI (JavaScript Interface)。

特性 Legacy 架構(gòu) (舊) 新架構(gòu) (Fabric + TurboModules)
JS 執(zhí)行環(huán)境 Bridge + CatalystInstance JSI + Hermes Runtime
Bundle 入口 ReactInstanceManager ReactHost / JSExecutorFactory
Native 管理器 ReactActivity / ReactRootView ReactHost / ReactInstance
JS 模塊 API 全局注入模塊 (Global Bridge) TurboModule + Codegen
支持情況 已過時 (Deprecated) 官方推薦

[!WARNING]
從 RN 0.72 起 Fabric 默認啟用,RN 0.80 之后已完全移除傳統(tǒng) Bridge 的 Fallback 機制。JSBundleLoader 等舊類在高性能新架構(gòu)下已無法直接使用。


二、 方案選擇:多 Runtime vs 單 Runtime

在 0.83.1 版本下,實現(xiàn)多模塊化主要有兩種思路:

  1. 多 ReactHost 實例化(隔離方案): 每個業(yè)務模塊獨立打包,包含獨立的基礎(chǔ)庫。優(yōu)點是隔離性強,缺點是內(nèi)存占用極高,無法共享基礎(chǔ)庫。
  2. 單 Hermes Runtime 動態(tài)加載(推薦方案):
    • 基礎(chǔ)包 (Base Bundle): 包含 RN 框架、NativeModules、公共庫及導航容器。
    • 業(yè)務包 (Business Bundle): 僅包含業(yè)務代碼,共用基礎(chǔ)包的運行時。
    • 優(yōu)勢: 內(nèi)存占用低、共用上下文、符合 Fabric 渲染鏈路。

本文采用方案 2 進行深度實踐。


三、 核心原理

  1. 按需加載: 啟動階段只加載 index.base.bundle。
  2. 動態(tài)注冊: 業(yè)務 Bundle 被加載時,通過 JSI 調(diào)用底層 C++ Runtime 的 evaluateJavaScript 執(zhí)行 JS 代碼,將業(yè)務頁面注冊到基礎(chǔ)包的導航器(如 React Navigation)中。
  3. 路由占位: 基礎(chǔ)包預留“殼頁面”(BizShell),當導航跳轉(zhuǎn)至業(yè)務模塊時,若 Bundle 未加載則觸發(fā) Native 動態(tài)加載流程。

四、 具體實現(xiàn)步驟

1. React Native 層實現(xiàn)

1.1 入口文件拆分

  • index.base.js (基礎(chǔ)包):

    import { AppRegistry } from 'react-native';
    import App from './src/App';
    import { name as appName } from './app.json';
    
    AppRegistry.registerComponent(appName, () => App);
    
  • index.buz1.js (業(yè)務包):

    import { registerBiz } from './src/navigation/DynamicRegistry';
    import Buz1Navigator from './src/biz1/Navigator';
    
    // 動態(tài)注冊業(yè)務路由
    registerBiz('buz1', Buz1Navigator);
    

1.2 路由占位邏輯

src/navigation/DynamicNavigator.tsx 中,使用 BizShell 作為業(yè)務占位符:

<Stack.Navigator initialRouteName={props.biz || 'NotFound'}>
  <Stack.Screen name="NotFound" component={NotFound} />
  
  {/* 業(yè)務占位:有多少個拆分包,就配置多少個 Screen */}
  <Stack.Screen 
    key={'buz1'} 
    name={'buz1'} 
    component={BizShell} 
    options={{ headerShown: false }} 
  />
  <Stack.Screen 
    key={'buz2'} 
    name={'buz2'} 
    component={BizShell} 
    options={{ headerShown: false }} 
  />
</Stack.Navigator>

提示: 核心路由跳轉(zhuǎn)與 Bundle 加載狀態(tài)維護均在 BizShell.tsx 中處理。


2. Android 端核心代碼

2.1 初始化 ReactHost

MainApplication 中手動啟動 ReactHost 并監(jiān)聽初始化狀態(tài)。

private fun initializeReactHost() {
    val host = reactHost
    host.start() // 啟動新架構(gòu) Host

    host.addReactInstanceEventListener(object : ReactInstanceEventListener {
        override fun onReactContextInitialized(reactContext: ReactContext) {
            // 將 Context 傳給動態(tài)加載器
            DynamicLoader.setContext(reactContext)
            try {
                // 執(zhí)行 JSI 綁定或其他初始化
                DynamicLoader.install()
                SplashActivity.instance?.hideLoading()
            } catch (e: Exception) {
                Log.e("MainApplication", "DynamicLoader 失?。?{e.message}")
            }
            host.removeReactInstanceEventListener(this)
        }
    })
}

2.2 動態(tài)加載 Bundle (NativeDynamicLoader.kt)

通過 JNI 獲取 C++ Runtime 句柄并執(zhí)行 JavaScript。

fun loadBundle(bundlePath: String) {
    val jsiPtr = context.javaScriptContextHolder.get()
    // 通過 JNI 調(diào)用 C++ 層的 evaluateJavaScript
    // 邏輯:判斷 bundlePath 是否已加載 -> 未加載則讀取文件并執(zhí)行
    nativeEvaluateJavaScript(jsiPtr, bundlePath)
}

3. iOS 端核心代碼

3.1 抽象 RNRuntimeManager

在 Swift 中管理 reactNativeFactoryreactNativeDelegate,通過 rootViewFactory 創(chuàng)建視圖。

3.2 動態(tài)加載 (RTNDynamicLoader)

在新架構(gòu)下,不再使用過時的 bridge.executeSourceCode。

// JSBundleLoader.swift 核心邏輯
if let runtimeExecutor = rootViewFactory.reactHost.surfacePresenter?.runtimeExecutor {
    // 拿到 RuntimeExecutor 后,在 JS 線程執(zhí)行業(yè)務代碼
    runtimeExecutor.execute { runtime in
        // 使用 C++ JSI 或內(nèi)部 API 執(zhí)行業(yè)務 Bundle 代碼
        loadBusinessBundle(path: bundlePath, in: runtime)
    }
}

五、 總結(jié)與優(yōu)勢

基于 RN 0.83.1 的這套拆包方案,完全擺脫了對 Legacy Bridge 的依賴:

  1. 極速加載: 利用 Hermes 字節(jié)碼預編譯,業(yè)務包加載近乎無感。
  2. 靈活分發(fā): 業(yè)務 Bundle 可獨立更新,無需重新安裝 App。
  3. 架構(gòu)對齊: 深度集成 ReactHostRuntimeExecutor,完美兼容 Fabric 渲染引擎,是目前新架構(gòu)下的最優(yōu)實踐。

發(fā)布建議:

  • 標簽: React Native, Android, iOS, 拆包, 新架構(gòu), Hermes.
  • 摘要: 針對 React Native 0.83.1 最新版本,詳細講解如何在 Fabric 架構(gòu)下實現(xiàn)單 Runtime 的業(yè)務拆包與動態(tài)加載。

項目git地址:https://gitee.com/liu_520/multi-bundle-programs

SVID_20251226_160340_1.gif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容