如何使用隔离方法将 Expo 添加到原生应用
编辑页面
一份关于将 Expo 和 React Native 作为原生库添加,并使用隔离方法将其集成到现有(brownfield)原生应用中的指南。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
在隔离式方案中,你的 React Native 代码是独立于原生项目进行开发和维护的。你将其打包为原生库,Android 使用 AAR,iOS 使用 XCFramework,并像其他依赖一样将其集成到原生应用中。
当你希望尽量减少 React Native 对现有原生构建流程的影响,或者原生开发与 React Native 开发由不同团队负责时,这种方案最为理想。使用这种方案后,原生开发者不需要 Node.js、Yarn 或任何 React Native 构建工具链,他们只需使用已经构建好的产物即可。
如果你想了解另一种将 React Native 直接集成到原生项目中的方案,请参阅 集成式方案指南。
前提条件
要将 React Native 集成到现有应用中,你需要先搭建 JavaScript 开发环境。这包括安装用于运行 Expo CLI 的 Node.js,以及用于管理项目 JavaScript 依赖的 Yarn。
- Node.js (LTS): 用于执行 JavaScript 代码和 Expo CLI 的运行时。
- Yarn: 用于安装和管理 JavaScript 依赖的包管理器。
更多信息请参阅 环境搭建指南。
设置 Expo 项目
1
2
3
调整配置插件(可选)
expo-brownfield 应会自动使用默认配置在你的 app.json 中的 plugins 数组里添加一项,这对于大多数项目来说已经足够。
{ "expo": { "plugins": ["expo-brownfield"] } }
默认值会根据你的应用配置推导而来(例如,目标名称会基于应用的 scheme 或 slug)。你也可以传入选项来自定义目标名称、包标识符和发布配置。
自定义 expo-brownfield 配置
{ "expo": { "plugins": [ [ "expo-brownfield", { "ios": { "targetName": "MyBrownfield", "bundleIdentifier": "com.example.mybrownfield" }, "android": { "libraryName": "mybrownfield", "group": "com.example", "package": "com.example.mybrownfield", "version": "1.0.0" } } ] ] } }
有关所有可用选项的详细信息,请参阅 expo-brownfield API 参考。
将 Expo 项目导出为原生库
完成 Expo 项目设置后,使用 expo-brownfield CLI 将你的 React Native 代码构建为 Android 的 AAR 和 iOS 的 XCFramework。
在你的 Expo 项目目录中运行:
- npx expo-brownfield build:android这会构建 AAR 并将其发布到 Maven 仓库。默认情况下,它会发布到你的本地 Maven 仓库(~/.m2),但也可以配置为发布到远程仓库。
生成的产物名称将由你的配置插件设置决定,在这个例子中是 com.username.myproject:brownfield:1.0.0。
有关构建选项的更多细节,例如仅构建 debug 或 release、指定自定义输出目录等,请参阅 API 参考。
在你的 Expo 项目目录中运行:
- npx expo-brownfield build:ios这会构建 XCFramework 产物:为设备和模拟器架构分别编译 framework 目标,将它们打包为 XCFramework,并复制 Hermes 引擎 framework。
构建过程完成后,输出会放在 ./artifacts 目录中,并包含:
- {TargetName}.xcframework - 你的 Expo 项目作为原生库
- hermesvm.xcframework - Hermes JavaScript 引擎
有关构建选项的更多细节,例如仅构建 debug 或 release、指定自定义输出目录等,请参阅 expo-brownfield API 参考。
调试原生目标
如果你需要调试 Expo 项目目标的原生代码,可以运行 npx expo prebuild 来生成带有 brownfield 库目标的原生项目,位于 android 和 ios 目录中。
- npx expo prebuild上述命令会生成以下内容:
- Android:一个单独的库模块,包含
ReactNativeHostManager、BrownfieldActivity、ReactNativeFragment、ReactNativeViewFactory和BrownfieldMessaging。 - iOS:一个单独的 Xcode framework 目标,包含
ReactNativeHostManager、ReactNativeViewController、ReactNativeView(SwiftUI)、BrownfieldMessaging和ReactNativeDelegate。
集成到你的原生应用中
在构建好产物后,你现在可以将它们集成到现有原生应用中。具体步骤将取决于你的项目结构和构建系统,但整体流程包括将预构建产物作为依赖添加进去,并初始化 React Native host。
1
添加 Maven 依赖
首先将依赖添加到你的应用 build.gradle.kts 中。group、artifact 名称和版本应与你的配置插件设置一致:
dependencies { implementation("com.username.myproject:brownfield:1.0.0") }
如果该库是发布到本地 Maven,请确保在仓库配置中添加 mavenLocal():
dependencyResolutionManagement { repositories { google() mavenCentral() mavenLocal() } }
2
显示 React Native 屏幕
创建一个继承自 BrownfieldActivity 的 activity,并使用 showReactNativeFragment() 扩展方法:
import android.os.Bundle import com.example.brownfield.BrownfieldActivity import com.example.brownfield.showReactNativeFragment class ExpoActivity : BrownfieldActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) showReactNativeFragment() } }
在你的 AndroidManifest.xml 中添加该 activity,并使用一个非 ActionBar 主题:
<activity android:name=".ExpoActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" />
然后在应用中的任意位置启动它:
startActivity(Intent(this, ExpoActivity::class.java))
BrownfieldActivity 继承自 AppCompatActivity,并负责将配置变化转发给 Expo 模块。showReactNativeFragment() 扩展方法也会自动配置原生返回按钮处理。
1
2
初始化 React Native
在应用生命周期的早期调用 ReactNativeHostManager.shared.initialize()。一个合适的位置是你的 AppDelegate:
import UIKit import MyAppBrownfield // 将其替换为你的 target 名称 @main class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { ReactNativeHostManager.shared.initialize() return true } }
3
展示 React Native 视图
import UIKit import MyAppBrownfield class ViewController: UIViewController { @IBAction func openReactNative(_ sender: Any) { let rnViewController = ReactNativeViewController(moduleName: "main") navigationController?.pushViewController(rnViewController, animated: true) } }
ReactNativeViewController 也接受可选的 initialProps 和 launchOptions 参数:
let rnViewController = ReactNativeViewController( moduleName: "main", initialProps: ["userId": "123"], launchOptions: [:] )
import SwiftUI import MyAppBrownfield struct ContentView: View { @State private var showReactNative = false var body: some View { Button("打开 React Native") { showReactNative = true } .fullScreenCover(isPresented: $showReactNative) { ReactNativeView(moduleName: "main") } } }
测试你的集成
你已经完成了将 React Native 集成到应用中的所有基础步骤。现在是时候测试一下了。具体流程取决于你运行的是 debug 还是 release 构建。
开发(debug 构建)
现在在 React Native 目录中运行以下命令,以启动 Metro bundler
- npx expo start然后,从 Android Studio 或 Xcode 构建并运行原生应用。当你进入 React Native 屏幕时,它会从 Metro 开发服务器加载,并支持热重载。
生产(release 构建)
在 release 构建中,JavaScript bundle 会嵌入到产物(AAR 或 XCFramework)中,因此不需要 Metro 服务器。以 Release 配置构建原生应用,并确认 React Native 屏幕能够正确加载。
下一步
为 Expo 模块配置应用生命周期监听器,以实现更深度的集成。
查看用于通信、导航等功能的完整 JavaScript API。