如何使用隔离方法将 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

创建新的 Expo 项目

运行以下命令创建一个名为 my-project 的新目录,其中包含你的新 Expo 项目。虽然你可以将项目命名为任意名称,但本指南为保持一致性,使用 my-project

Terminal
npx create-expo-app@latest my-project --template default@sdk-55

my-project 不需要放在现有原生应用内部,也可以创建在单独的仓库中或 monorepo 中。新项目包含一个示例 TypeScript 应用,帮助你快速开始。

2

安装 expo-brownfield

进入你的新 Expo 项目并安装 expo-brownfield 库,它提供了将 React Native 代码构建为原生库并集成到现有原生应用中的工具。

Terminal
npx expo install expo-brownfield

3

调整配置插件(可选)

expo-brownfield 应会自动使用默认配置在你的 app.json 中的 plugins 数组里添加一项,这对于大多数项目来说已经足够。

app.json
{ "expo": { "plugins": ["expo-brownfield"] } }

默认值会根据你的应用配置推导而来(例如,目标名称会基于应用的 scheme 或 slug)。你也可以传入选项来自定义目标名称、包标识符和发布配置。

自定义 expo-brownfield 配置
app.json
{ "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 项目目录中运行:

Terminal
npx expo-brownfield build:android

这会构建 AAR 并将其发布到 Maven 仓库。默认情况下,它会发布到你的本地 Maven 仓库(~/.m2),但也可以配置为发布到远程仓库。 生成的产物名称将由你的配置插件设置决定,在这个例子中是 com.username.myproject:brownfield:1.0.0

有关构建选项的更多细节,例如仅构建 debug 或 release、指定自定义输出目录等,请参阅 API 参考

在你的 Expo 项目目录中运行:

Terminal
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 库目标的原生项目,位于 androidios 目录中。

Terminal
npx expo prebuild

上述命令会生成以下内容:

  • Android:一个单独的库模块,包含 ReactNativeHostManagerBrownfieldActivityReactNativeFragmentReactNativeViewFactoryBrownfieldMessaging
  • iOS:一个单独的 Xcode framework 目标,包含 ReactNativeHostManagerReactNativeViewControllerReactNativeView(SwiftUI)、BrownfieldMessagingReactNativeDelegate

集成到你的原生应用中

在构建好产物后,你现在可以将它们集成到现有原生应用中。具体步骤将取决于你的项目结构和构建系统,但整体流程包括将预构建产物作为依赖添加进去,并初始化 React Native host。

1

添加 Maven 依赖

首先将依赖添加到你的应用 build.gradle.kts 中。group、artifact 名称和版本应与你的配置插件设置一致:

app/build.gradle.kts
dependencies { implementation("com.username.myproject:brownfield:1.0.0") }

如果该库是发布到本地 Maven,请确保在仓库配置中添加 mavenLocal()

settings.gradle.kts
dependencyResolutionManagement { repositories { google() mavenCentral() mavenLocal() } }

2

显示 React Native 屏幕

创建一个继承自 BrownfieldActivity 的 activity,并使用 showReactNativeFragment() 扩展方法:

ExpoActivity.kt
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 主题:

AndroidManifest.xml
<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

将 XCFramework 添加到项目中

将两个 XCFramework 文件({TargetName}.xcframeworkhermesvm.xcframework)拖入 Xcode 项目导航器。在弹出的对话框中:

  • 勾选 Copy items if needed
  • 将它们添加到你的 app target

然后,在你的 target 的 General 选项卡中,找到 Frameworks, Libraries, and Embedded Content,确保这两个 framework 都设置为 Embed & Sign

2

初始化 React Native

在应用生命周期的早期调用 ReactNativeHostManager.shared.initialize()。一个合适的位置是你的 AppDelegate

AppDelegate.swift
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 视图

ViewController.swift
import UIKit import MyAppBrownfield class ViewController: UIViewController { @IBAction func openReactNative(_ sender: Any) { let rnViewController = ReactNativeViewController(moduleName: "main") navigationController?.pushViewController(rnViewController, animated: true) } }

ReactNativeViewController 也接受可选的 initialPropslaunchOptions 参数:

let rnViewController = ReactNativeViewController( moduleName: "main", initialProps: ["userId": "123"], launchOptions: [:] )
ContentView.swift
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

Terminal
npx expo start

然后,从 Android Studio 或 Xcode 构建并运行原生应用。当你进入 React Native 屏幕时,它会从 Metro 开发服务器加载,并支持热重载。

生产(release 构建)

在 release 构建中,JavaScript bundle 会嵌入到产物(AAR 或 XCFramework)中,因此不需要 Metro 服务器。以 Release 配置构建原生应用,并确认 React Native 屏幕能够正确加载。

下一步

生命周期监听器

为 Expo 模块配置应用生命周期监听器,以实现更深度的集成。

expo-brownfield API 参考

查看用于通信、导航等功能的完整 JavaScript API。