React Native 0.83.1 新架构(New Architecture) 下的拆包方案
React Native 0.83.1 新架构拆包实战:基于 ReactHost 与单 Runtime 的动态加载方案
随着 React Native 进入 0.80+ 时代,新架构(Fabric & TurboModules)已成为主流。传统的基于 ReactInstanceManager 和 Bridge 的拆包方式已逐步过时。本文将介绍在 RN 0.83.1 版本下,如何利用 ReactHost、JSI 和 Hermes Runtime 实现高性能的模块化拆包与动态加载。
一、 为什么旧的拆包方案失效了?
在新架构中,底层通信机制从传统的 JSON Bridge 转向了基于 C++ 的 JSI (JavaScript Interface)。
| 特性 | Legacy 架构 (旧) | 新架构 (Fabric + TurboModules) |
|---|---|---|
| JS 执行环境 | 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 之后已完全移除传统 Bridge 的 Fallback 机制。
JSBundleLoader等旧类在高性能新架构下已无法直接使用。
二、 方案选择:多 Runtime vs 单 Runtime
在 0.83.1 版本下,实现多模块化主要有两种思路:
- 多 ReactHost 实例化(隔离方案): 每个业务模块独立打包,包含独立的基础库。优点是隔离性强,缺点是内存占用极高,无法共享基础库。
- 单 Hermes Runtime 动态加载(推荐方案):
- 基础包 (Base Bundle): 包含 RN 框架、NativeModules、公共库及导航容器。
- 业务包 (Business Bundle): 仅包含业务代码,共用基础包的运行时。
- 优势: 内存占用低、共用上下文、符合 Fabric 渲染链路。
本文采用方案 2 进行深度实践。
三、 核心原理
- 按需加载: 启动阶段只加载
index.base.bundle。 - 动态注册: 业务 Bundle 被加载时,通过 JSI 调用底层 C++ Runtime 的
evaluateJavaScript执行 JS 代码,将业务页面注册到基础包的导航器(如 React Navigation)中。 - 路由占位: 基础包预留"壳页面"(BizShell),当导航跳转至业务模块时,若 Bundle 未加载则触发 Native 动态加载流程。
四、 具体实现步骤
1. React Native 层实现
1.1 入口文件拆分
-
index.base.js (基础包):
javascriptimport { AppRegistry } from 'react-native'; import App from './src/App'; import { name as appName } from './app.json'; AppRegistry.registerComponent(appName, () => App); -
index.buz1.js (业务包):
javascriptimport { registerBiz } from './src/navigation/DynamicRegistry'; import Buz1Navigator from './src/biz1/Navigator'; // 动态注册业务路由 registerBiz('buz1', Buz1Navigator);
1.2 路由占位逻辑
在 src/navigation/DynamicNavigator.tsx 中,使用 BizShell 作为业务占位符:
tsx
<Stack.Navigator initialRouteName={props.biz || 'NotFound'}>
<Stack.Screen name="NotFound" component={NotFound} />
{/* 业务占位:有多少个拆分包,就配置多少个 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>
提示: 核心路由跳转与 Bundle 加载状态维护均在
BizShell.tsx中处理。
2. Android 端核心代码
2.1 初始化 ReactHost
在 MainApplication 中手动启动 ReactHost 并监听初始化状态。
kotlin
private fun initializeReactHost() {
val host = reactHost
host.start() // 启动新架构 Host
host.addReactInstanceEventListener(object : ReactInstanceEventListener {
override fun onReactContextInitialized(reactContext: ReactContext) {
// 将 Context 传给动态加载器
DynamicLoader.setContext(reactContext)
try {
// 执行 JSI 绑定或其他初始化
DynamicLoader.install()
SplashActivity.instance?.hideLoading()
} catch (e: Exception) {
Log.e("MainApplication", "DynamicLoader 失败:${e.message}")
}
host.removeReactInstanceEventListener(this)
}
})
}
2.2 动态加载 Bundle (NativeDynamicLoader.kt)
通过 JNI 获取 C++ Runtime 句柄并执行 JavaScript。
kotlin
fun loadBundle(bundlePath: String) {
val jsiPtr = context.javaScriptContextHolder.get()
// 通过 JNI 调用 C++ 层的 evaluateJavaScript
// 逻辑:判断 bundlePath 是否已加载 -> 未加载则读取文件并执行
nativeEvaluateJavaScript(jsiPtr, bundlePath)
}
3. iOS 端核心代码
3.1 抽象 RNRuntimeManager
在 Swift 中管理 reactNativeFactory 和 reactNativeDelegate,通过 rootViewFactory 创建视图。
3.2 动态加载 (RTNDynamicLoader)
在新架构下,不再使用过时的 bridge.executeSourceCode。
swift
// JSBundleLoader.swift 核心逻辑
if let runtimeExecutor = rootViewFactory.reactHost.surfacePresenter?.runtimeExecutor {
// 拿到 RuntimeExecutor 后,在 JS 线程执行业务代码
runtimeExecutor.execute { runtime in
// 使用 C++ JSI 或内部 API 执行业务 Bundle 代码
loadBusinessBundle(path: bundlePath, in: runtime)
}
}
五、 总结与优势
基于 RN 0.83.1 的这套拆包方案,完全摆脱了对 Legacy Bridge 的依赖:
- 极速加载: 利用 Hermes 字节码预编译,业务包加载近乎无感。
- 灵活分发: 业务 Bundle 可独立更新,无需重新安装 App。
- 架构对齐: 深度集成
ReactHost和RuntimeExecutor,完美兼容 Fabric 渲染引擎,是目前新架构下的最优实践。
发布建议:
- 标签: React Native, Android, iOS, 拆包, 新架构, Hermes.
- 摘要: 针对 React Native 0.83.1 最新版本,详细讲解如何在 Fabric 架构下实现单 Runtime 的业务拆包与动态加载。
项目git地址:gitee.com/liu_520/mul...
