深入解析 install-expo-modules:让既有 React Native 项目平滑接入 Expo Modules
install-expo-modules 是 expo/expo 仓库中专门服务于存量 React Native 项目的迁移工具:只需在项目根目录执行一条 npx install-expo-modules 命令,它就会自动安装 expo 核心包、修改 Android/iOS 原生工程与 Babel/Metro 配置,让纯 React Native(RNC CLI)项目无缝获得 expo-modules 与 Expo SDK 的能力。读完本文,你将掌握该工具的使用方式、背后 SDK 版本映射与原生工程改写机制,并了解其版本演进脉络与贡献方法。
一、工具定位与快速上手
根据 packages/install-expo-modules/README.md,该工具面向"已经存在的 React Native 项目",帮助其更轻松地采用 expo-modules 与 Expo SDK。使用方式极其简单——在项目根目录执行:
npx install-expo-modules
执行完成后,就可以通过 expo install 安装你需要的具体 expo 模块,例如:
expo install expo-device
# 注意:expo 命令来自 expo-cli;若未安装,可先执行 `npm -g install expo-cli`
工具的核心入口位于 src/index.ts,CLI 基于 commander 构建,其可用的命令行参数包括:
| 参数 | 说明 |
|---|---|
[project-directory] |
目标项目目录,缺省时使用当前工作目录(process.cwd()) |
-s, --sdk-version <version> |
指定要安装的 Expo SDK 版本,不指定则根据项目 React Native 版本自动推导 |
--non-interactive |
禁用交互式确认提示,适用于 CI 等自动化场景 |
二、工具为项目做了什么
README 明确列出了 install-expo-modules 完成的工作清单,结合 src/index.ts 的 runAsync 主流程,实际执行链路如下:
- 定位项目根目录并探测平台:
normalizeProjectRootAsync会向上查找package.json确定项目根目录,并检测是否存在android/ios目录来决定要改写的平台(见 src/utils/projectRoot.ts)。 - 安装
expo包:安装必要的核心包并启用 react-native 自动链接(autolinking),为后续模块安装打好基础(见 src/utils/packageInstaller.ts 的installExpoPackageAsync,它会优先安装正式发布版本,失败时回退到预发布版本范围)。 - 修改项目文件以适配 expo-modules:如果项目由 git 管理,完全可以用
git diff审查工具所做的每一项改动,做到"所见即所得"。 - 升级 iOS 部署目标:由于 expo-modules 的最低 iOS 版本要求可能高于 React Native 核心,当现有部署目标偏低时,工具会自动将其提升。
- 最后执行
pod install:为 iOS 更新链接的模块。从源码看,这一步只在process.platform === 'darwin'(macOS)上执行,与 CHANGELOG 中 0.14.19 的"Skip pods install on non-darwin platforms"改动相对应。
整个过程通过 @expo/config-plugins 的 compileModsAsync 编译配置修改器(mods)完成,执行顺序是先改写工程文件、再安装 npm 包、最后装 Pods。
三、核心机制:SDK 版本映射表
工具最关键的机制是 Expo SDK 与 React Native 版本之间的映射,定义在 src/utils/expoVersionMappings.ts 的 ExpoVersionMappings 数组中。每个条目包含:
expoPackageVersion:要安装的exponpm 包版本范围;sdkVersion:对应的 Expo SDK 版本号;iosDeploymentTarget:该 SDK 要求的最低 iOS 部署目标;reactNativeVersionRange:兼容的 React Native 版本范围(semver 表达式);androidAgpVersion(可选):最低 Android Gradle Plugin 版本要求;supportCliIntegration(可选):是否支持 Expo CLI 集成。
当前映射表内容(截至仓库中 0.16.0 版本):
| Expo SDK | expo 包版本 | RN 版本范围 | 最低 iOS 目标 |
|---|---|---|---|
| 56.0.0 | ~56.0.0 |
~0.85.0 |
16.4 |
| 55.0.0 | ~55.0.0 |
~0.83.0 |
15.1 |
| 54.0.0 | ~54.0.0 |
~0.81.0 |
15.1 |
| 53.0.0 | ~53.0.0 |
~0.79.0 |
15.1 |
| 52.0.0 | ~52.0.0 |
>= 0.76.0 < 0.78.0 |
15.1 |
| 51.0.0 | ~51.0.0 |
>= 0.74.0 < 0.76.0 |
13.4 |
| 50.0.0 | ~50.0.0 |
~0.73.0 |
13.4 |
| 49.0.0 | ~49.0.0 |
~0.72.0 |
13.0 |
| 48.0.0 | ~48.0.0 |
~0.71.0 |
13.0(AGP 7.4.1) |
| 47.0.0 | ~47.0.0 |
~0.70.0 |
13.0 |
| 46.0.0 | ~46.0.0 |
~0.69.0 |
12.4 |
| 45.0.0 | ~45.0.0 |
>= 0.65.0 < 0.69.0 |
12.0 |
| 44.0.0 | ~44.0.0 |
< 0.68.0 |
12.0 |
| 43.0.0 | ~43.0.0 |
< 0.68.0 |
12.0 |
当未通过 -s 指定 SDK 版本时,getDefaultSdkVersion 会读取项目 react-native/package.json 的实际版本,用 semver.satisfies 与映射表匹配,自动选择兼容的 SDK;找不到时会抛出明确的错误提示。这种"以 RN 版本反推 SDK"的设计,正是工具能对存量项目做到零配置迁移的关键。
四、Android 侧工程改写细节
在 Android 平台上,工具通过多个 config plugin 完成原生代码改造:
1. settings.gradle 自动链接
src/plugins/android/withAndroidSettingsGradle.ts 会在 pluginManagement 中注入 expo-modules-autolinking 的解析逻辑与 includeBuild,注册 expo-autolinking-settings 插件,并将 ex.autolinkLibrariesFromCommand() 改写为 ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand),最后追加 useExpoModules()、useExpoVersionCatalog() 与 includeBuild(expoAutolinking.reactNativeGradlePlugin)。对于 SDK 53 之前的项目,则走另一套基于 autolinking.gradle 脚本的兼容路径(见文件中的 updateAndroidSettingsGradleSdk52)。
2. MainApplication 改造
src/plugins/android/withAndroidModulesMainApplication.ts 展示了工具对 MainApplication 的精细化改写逻辑,且能同时处理 Java 与 Kotlin:
- 为
DefaultReactNativeHost(RN ≥ 0.71)与ReactNativeHost实例注入ReactNativeHostWrapper包装; - SDK ≥ 55 时将
getDefaultReactHost()替换为ExpoReactHostFactory.getDefaultReactHost(),SDK 51–54 则替换为ReactNativeHostWrapper.createReactHost(); - 注入
ApplicationLifecycleDispatcher的onApplicationCreate调用,并在缺失时补全onConfigurationChanged重写,确保 Expo 模块能感知应用生命周期与配置变化。
3. 构建脚本与 AGP 版本
src/plugins/android/withAndroidGradles.ts 负责 Gradle 版本相关调整。从 src/index.ts 可以看到,当检测到项目 AGP 版本低于 Expo 模块要求时,工具会弹出确认提示(--non-interactive 模式下直接以黄色警告输出并继续),确认后才将 AGP 提升到要求版本。iOS 部署目标升级也采用同样的交互模式,两条提示分别对应 withAndroidGradlePluginVersion 与 withIosDeploymentTarget。
五、iOS 侧工程改写细节
iOS 平台同样由一系列插件驱动:
- AppDelegate 改造:见 src/plugins/ios/withIosModulesAppDelegate.ts。该插件同时支持 Objective-C/ObjC++ 与 Swift 三种语言形态:ObjC 工程会把父类替换为
EXAppDelegateWrapper并补全[super application:...]调用与<Expo/Expo.h>导入;Swift 工程则根据 SDK 版本选择不同的改造策略——SDK 52 使用ExpoModulesCore中的ExpoAppDelegate,SDK 55+ 进一步使用internal import Expo以兼容 Swift 6,并替换RCTReactNativeFactory/RCTDefaultReactNativeFactoryDelegate为 Expo 对应类。 - Podfile 处理:withIosModulesPodfile.ts 负责注入 Expo 相关 pod 配置。
- Swift 版本与部署目标:
withSwiftVersion统一将 Swift 版本设置为 5.0;withIosDeploymentTarget按映射表提升部署目标。 - Xcode 工程解析:通过
xcparse(见 withXCParseXcodeProject.ts)直接解析.pbxproj,用于查找 Swift bridging header 文件引用、扫描PBXShellScriptBuildPhase等,这也是 0.14.19 之前 CHANGELOG 中多次修复 Xcode 工程问题的底层技术。
六、可选的 Expo CLI 集成
工具会在交互模式下询问是否安装 Expo CLI 集成(见 src/index.ts 的 promptCliIntegrationAsync,推荐启用,否则部分功能可能无法按预期工作)。选择启用后,src/plugins/cli/withCliIntegration.ts 会通过 withPlugins 依次执行六项子改动:
- 改写
app/build.gradle(仅支持 groovy 语法); - 为 Android
MainApplication与 iOSAppDelegate设置虚拟的 Metro 入口; - 在 Xcode 工程的
PBXShellScriptBuildPhase中加入 Expo 相关脚本; - 更新 Babel 配置以使用
babel-preset-expo(找不到配置文件时给出警告); - 更新 Metro 配置并处理
.gitignore。
若启用 CLI 集成,还会额外安装 babel-preset-expo(见 packageInstaller.ts 的 installBabelPresetExpoNonInteractiveAsync)。
七、版本演进时间线(来自 CHANGELOG)
packages/install-expo-modules/CHANGELOG.md 完整记录了该工具的演进历程,从中可以清晰地看到它与 Expo SDK / React Native 版本同步的节奏:
- 2024 年(0.7.0–0.10.2):包从
expo/expo-cli迁移到expo/expo仓库(0.7.0);加入 Expo SDK 50 / RN 0.73 支持(0.8.0);修复 CNG 项目上的ENOENT误报与 Yarn v3 语法错误、避免在npx install-expo-modules时重复安装依赖(0.8.1);加入 SDK 51 / RN 0.74 支持(0.10.0)。 - 2024 年底(0.11.x):0.11.0 将 iOS 部署目标提升至 15.1,支持除
babel.config.js外的其他 Babel 配置文件名、加入 RN 0.76 支持,并修复 RNC CLI 项目的 "Unsupported Swift Version" 问题与缺失babel.config.js时的崩溃。 - 2025 年(0.12.x–0.13.x):0.12.0 一次性加入 RN 0.77/0.78/0.79 支持;0.12.3 更新了 SDK 53 的
AppDelegate.swift改造逻辑;0.13.10 加入 RN 0.81 与 SDK 54 支持;0.13.13 将glob升级到 v13。 - 2026 年(0.14.x–0.16.0):0.14.0 起进入高频维护期;0.14.5 允许 React Native TV 项目使用;0.14.10/0.14.11 加入 SDK 55 / RN 0.83 支持;0.14.19 将最低 iOS/tvOS 版本提升至 16.4、macOS 提升至 13.4,并在非 darwin 平台跳过 pods 安装;0.15.0 升级
@expo/spawn-async;未发布的版本还包含对偶发ncc构建失败的修复。从 0.16.0 之后的多条 "no user-facing changes" 记录可以看出,工具在功能稳定后进入了以依赖维护为主的状态。
这条时间线同时也回答了"我的 React Native 版本能不能用"的问题:由于映射表持续更新,从 RN 0.65 时代的老项目到 0.85 的新项目都能找到对应的 SDK 版本。
八、测试保障与贡献方式
工具的每个改写插件都配有单元测试与 fixture 对照文件,例如:
- Android 侧:
MainActivity/MainApplication的 Java/Kotlin fixture 覆盖了 RN 0.64、0.68、0.71、0.73、0.74、0.83 等不同版本形态,见 plugins/android/tests; - iOS 侧:
AppDelegate的.m/.mm/.h/.swift与 Podfile fixture 覆盖 RN 0.67–0.83 各代工程,见 plugins/ios/tests; - CLI 集成侧:通过 snapshot 测试校验
AppDelegate、MainApplication、gradle、Babel、Metro 与.pbxproj的完整改写结果,见 plugins/cli/tests。
SDK 版本映射也有专门的测试(utils/tests/expoVersionMappings-test.ts)。若你希望为本工具贡献改动,README 给出了推荐流程:先在仓库内运行 pnpm watch 以监听模式构建,改动后在一个 RNC CLI 测试项目里执行 node path_to_expo/packages/install-expo-modules/bin/install-expo-modules.js . 验证,并记得为改动补充单元测试。
九、小结
install-expo-modules 通过"SDK 版本映射表 + config-plugins 工程改写 + 交互式确认"三件套,把原本繁琐、易错的手工迁移变成了可复现、可审查的一键操作。无论是老项目想要用上 Expo 生态的模块,还是团队希望统一到 Expo SDK 的管理方式,这条迁移路径都值得优先考虑:执行一条命令,用 git diff 审查改动,再用 expo install 按需添加模块,即可完成从纯 React Native 到 expo-modules 的平滑过渡。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0632
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00