Nx 工作区中 Expo SDK 53 升级迁移到 SDK 54 的完整指南
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
导读:本文以 Nx 官方仓库 ai-instructions-for-expo-54.md 为核心骨架,系统讲解如何将一个包含多个 Expo 应用的 Nx Monorepo 工作区从 Expo SDK 53 平滑迁移到 SDK 54。文章覆盖迁移前检查、九大类破坏性变更的逐一处理、Jest 与版本对齐、迁移后验证以及常见问题排查,并补充
@nx/expo插件源码级证据(迁移实现与测试),帮助读者(尤其是 AI Agent/LLM 执行者)按部就班地完成一次可验证、可回滚的 SDK 升级。
在 Nx 仓库中,这份文档并非普通说明,而是被注册为一条可执行迁移指令:在 migrations.json 中,update-22-2-0-create-ai-instructions-for-expo-54迁移通过prompt字段引用该 Markdown 文件,同时配套update-22-2-0-add-expo-system-ui、update-22-2-0-update-jest-for-expo-54两条自动化迁移(均要求expo >= 54.0.0),以及22.2.0的packageJsonUpdates版本更新。也就是说,Nx 既提供自动化的代码改动,也把这份"给 LLM 的迁移指引"作为执行手册交付给 AI Agent。理解这一点,有助于读者把它当作一套"先自动化、后人工核对"的完整升级流程。
迁移前检查清单(Pre-Migration Checklist)
在动手之前,先完成四项摸底工作,确定迁移的波及范围:
1. 识别工作区中的所有 Expo 项目:Expo 应用通常带有start目标,因此可以一次性列出:
nx show projects --with-target start2. 定位所有 Expo 配置文件:
- 搜索
app.json或app.config.{js,ts} - 搜索
metro.config.{js,ts} - 检查各项目的
project.json中是否包含 Expo 相关配置(如start、run-ios、run-android、export、prebuild等目标)
3. 识别受影响代码:
- 从
expo-av导入的文件(音频/视频功能) - 从
expo-file-system导入的文件 - 使用
StatusBar配置的代码 - Android 特有的布局代码(涉及安全区域)
- 从
@expo/vector-icons导入的文件
4. 检查是否存在 Detox E2E 测试项目:
- 在
package.json依赖中搜索detox - 查找
detox.config.js或.detoxrc.js文件
建议同时记录每个应用
package.json中expo的当前版本号,作为迁移前后对比的基线。
重要警告:Detox E2E 测试暂不支持
Expo SDK 54 目前不支持 Detox 进行端到端测试。如果工作区包含 Detox 项目,需要明确以下影响:
- 影响:迁移到 Expo SDK 54 后,使用 Detox 的 E2E 测试将无法工作。
- 检测方式:检查
package.json中是否存在detox依赖,或项目中是否存在detox.config.js。
必须执行的动作:检测到 Detox 项目时,务必先征询用户意见,再继续迁移。建议的询问文案(可直接引用):
"Your workspace contains Detox E2E tests. Detox is not currently supported in Expo SDK 54. Do you want to proceed with the migration knowing that Detox tests will not work until Detox adds SDK 54 support?"
可考虑的替代方案:
- 如果 Detox E2E 测试对工作流至关重要,留在 Expo SDK 53;
- 等待 Detox 增加 SDK 54 支持后再升级;
- 考虑迁移到Maestro做 E2E 测试(已支持 Expo SDK 54)。
迁移步骤:九大类破坏性变更逐一处理
1. expo-av 拆分迁移到 expo-audio 与 expo-video
expo-av已被弃用,拆分为expo-audio与expo-video两个独立包。搜索模式:import.*from ['"]expo-av['"],根据实际用途分别处理。
1.1 音频迁移
// BEFORE (Expo SDK 53) import { Audio } from 'expo-av'; const sound = new Audio.Sound(); await sound.loadAsync(require('./audio.mp3')); await sound.playAsync(); // AFTER (Expo SDK 54) import { useAudioPlayer } from 'expo-audio'; function AudioComponent() { const player = useAudioPlayer(require('./audio.mp3')); const play = () => player.play(); const pause = () => player.pause(); return <Button onPress={play} title="Play" />; }行动清单:
- 安装
expo-audio:npx expo install expo-audio - 若
expo-av仅用于音频,将其移除 - 用
useAudioPlayerHook 替换Audio.Sound - 用
useAudioRecorderHook 替换Audio.Recording - 更新播放控制方法(
playAsync→play等) - 将基于类的音频处理改为基于 Hook 的写法
1.2 视频迁移
// BEFORE (Expo SDK 53) import { Video } from 'expo-av'; function VideoPlayer() { return ( <Video source={{ uri: 'https://example.com/video.mp4' }} style={{ width: 300, height: 200 }} useNativeControls resizeMode="contain" /> ); } // AFTER (Expo SDK 54) import { VideoView, useVideoPlayer } from 'expo-video'; function VideoPlayer() { const player = useVideoPlayer('https://example.com/video.mp4', (player) => { player.loop = true; player.play(); }); return ( <VideoView player={player} style={{ width: 300, height: 200 }} nativeControls contentFit="contain" /> ); }行动清单:
- 安装
expo-video:npx expo install expo-video - 若
expo-av仅用于视频,将其移除 - 用
VideoView+useVideoPlayer替换Video组件 - 将
resizeMode属性替换为contentFit - 将
useNativeControls属性替换为nativeControls - 视频控制方法改用 player 实例调用
2. expo-file-system 导入路径变更
Expo SDK 54 中,expo-file-system的新 API(此前位于/next子路径)已转为稳定 API,导入路径相应简化。搜索模式:import.*from ['"]expo-file-system/next['"]。
// BEFORE (Expo SDK 53) import { File, Directory } from 'expo-file-system/next'; // AFTER (Expo SDK 54) import { File, Directory } from 'expo-file-system';行动清单:
- 将所有
expo-file-system/next导入替换为expo-file-system - 验证 API 兼容性(该 API 现已稳定)
3. StatusBar 配置移除(改用 expo-system-ui)
SDK 54 中expo-status-bar的配置方式发生变化,app.json中的userInterfaceStyle会直接影响状态栏主题。搜索模式:app.json或app.config.*中的userInterfaceStyle。
// BEFORE (Expo SDK 53) { "expo": { "userInterfaceStyle": "automatic", "ios": { "userInterfaceStyle": "light" }, "android": { "userInterfaceStyle": "dark" } } } // AFTER (Expo SDK 54) // userInterfaceStyle 改由 expo-system-ui 处理 // 如需程序化控制,使用:// AFTER (Expo SDK 54) - 程序化控制 import * as SystemUI from 'expo-system-ui'; // 设置根视图背景色 SystemUI.setBackgroundColorAsync('#ffffff');行动清单:
- 安装
expo-system-ui:npx expo install expo-system-ui - 审查
app.json中的userInterfaceStyle设置 - 如需程序化控制,将 UI 风格处理迁移到
expo-system-ui
源码佐证:Nx 为此提供了自动化迁移 add-expo-system-ui.ts,其逻辑是遍历getProjects得到的所有项目,仅对projectType === 'application'且package.json中存在expo依赖的 Expo 应用,在其dependencies中补写expo-system-ui: '~6.0.0'(若已存在则跳过)。对应测试 add-expo-system-ui.spec.ts 覆盖了"为 Expo 应用添加依赖""已存在时不覆盖""跳过非 Expo 项目""跳过 library 项目""无 package.json 时不抛错"五种场景,可见该迁移设计得足够保守。
4. Android Edge-to-Edge UI(默认开启)
Expo SDK 54 在 Android 上默认启用 edge-to-edge 显示,内容会延伸到系统栏(状态栏和导航栏)之下。搜索模式:Android 特有样式、安全区域处理、padding/margin 调整。
// BEFORE (Expo SDK 53) - 隐式安全区域 function App() { return ( <View style={{ flex: 1 }}> <Text>Content</Text> </View> ); } // AFTER (Expo SDK 54) - 显式安全区域处理 import { SafeAreaView } from 'react-native-safe-area-context'; // 或 import { useSafeAreaInsets } from 'react-native-safe-area-context'; function App() { return ( <SafeAreaView style={{ flex: 1 }}> <Text>Content</Text> </SafeAreaView> ); } // 或者用 Hook 获得更精细的控制 function App() { const insets = useSafeAreaInsets(); return ( <View style={{ flex: 1, paddingTop: insets.top, paddingBottom: insets.bottom }} > <Text>Content</Text> </View> ); }行动清单:
- 审计所有屏幕组件的安全区域处理
- 若未安装,安装
react-native-safe-area-context - 用
SafeAreaProvider包裹根组件 - 在内容触及屏幕边缘处添加
SafeAreaView或使用useSafeAreaInsets - 在 Android 真机/模拟器上验证 UI 不与系统栏重叠
- 特别关注以下位置:
- 顶部 Header 组件
- 底部导航 / Tab 栏
- Modal 组件
- 全屏媒体播放器
5. React Native Reanimated 版本决策
Expo SDK 54 同时支持 Reanimated v3 与 v4。如果使用 New Architecture,应升级到 v4。搜索模式:package.json中的react-native-reanimated、worklet 函数。
Reanimated v3(稳定版):
npx expo install react-native-reanimated@3Reanimated v4(New Architecture 推荐):
npx expo install react-native-reanimated@4行动清单:
- 检查项目是否启用了 New Architecture
- 使用 New Architecture 则升级到 Reanimated v4
- 停留在旧架构则继续使用 Reanimated v3
- 升级后全面测试所有动画
6. @expo/vector-icons 校验
SDK 54 中@expo/vector-icons的部分图标可能被重命名或移除。搜索模式:import.*from ['"]@expo/vector-icons['"]。
行动清单:
- 运行应用,在控制台检查缺失图标的警告
- 搜索可能变更的图标名称
- 按需更新图标名称
- 参考 Expo 官方 Vector Icons 目录(
icons.expo.fyi)查找替代图标
7. React 19.1 兼容性
Expo SDK 54 使用React 19.1和React Native 0.81。搜索模式:React.FC、已废弃的生命周期方法、旧版 Context API。
// BEFORE - React.FC(React 19 中不推荐) const MyComponent: React.FC<Props> = ({ title }) => { return <Text>{title}</Text>; }; // AFTER - 直接函数类型标注 function MyComponent({ title }: Props) { return <Text>{title}</Text>; } // 或显式声明返回类型 const MyComponent = ({ title }: Props): React.ReactElement => { return <Text>{title}</Text>; };行动清单:
- 为 React 19.1 更新 TypeScript 类型(
@types/react@~19.1.0) - 移除
React.FC模式(可选但推荐) - 检查废弃的生命周期方法并改用 Hooks
- 验证第三方库与 React 19.1 的兼容性
8. Metro 配置更新
Expo SDK 54 使用Metro 0.83并带更新的配置。搜索模式:metro.config.{js,ts}。
// BEFORE (Expo SDK 53) const { getDefaultConfig } = require('@expo/metro-config'); const config = getDefaultConfig(__dirname); // AFTER (Expo SDK 54) - API 相同,但需验证兼容性 const { getDefaultConfig } = require('@expo/metro-config'); const config = getDefaultConfig(__dirname); // 确保自定义 transformer 兼容 Metro 0.83行动清单:
- 更新
@expo/metro-config - 更新
metro-config至~0.83.0 - 更新
metro-resolver至~0.83.0 - 测试自定义 Metro 插件/transformer 的兼容性
9. Babel 配置清理
Expo SDK 54 将babel-preset-expo升级到新版本。搜索模式:babel.config.js、.babelrc。
行动清单:
- 更新
babel-preset-expo版本 - 移除任何已废弃的 Babel 插件
- Babel 变更后清空 Metro 缓存:
npx expo start --clear
目标版本对齐:以仓库 migrations.json 为准
原迁移文档给出的是通用指引,而 Nx 仓库本身维护着一份精确到每个依赖的版本常量表。在执行第 7~9 类变更时,应参照 migrations.json 中22.2.0的packageJsonUpdates(要求expo >=53.0.0 <54.0.0)以及 versions.ts 中 Expo v54 常量,逐项对齐版本:
| 依赖 | SDK 53(升级前) | SDK 54(升级目标) |
|---|---|---|
expo | ~53.0.10 | ~54.0.0 |
react | ^19.0.0 | ^19.1.0 |
react-native | ~0.79.3 | 0.81.5 |
@types/react | ~19.0.10 | ^19.1.0 |
react-native-web | ~0.20.0 | ~0.21.0 |
expo-system-ui | ~5.0.8 | ~6.0.8 |
expo-status-bar | ~2.2.3 | ~3.0.8 |
expo-splash-screen | ~0.30.9 | ~31.0.11 |
@expo/cli | ~0.24.14 | ~54.0.16 |
babel-preset-expo | ~13.2.0 | ~54.0.7 |
jest-expo | ~53.0.7 | ~54.0.13 |
@expo/metro-config | ~0.20.14 | ~54.0.9 |
metro-config | — | ~0.83.0 |
metro-resolver | — | ~0.83.0 |
@testing-library/react-native | ~13.2.0 | ~13.2.0 |
两点说明:
- 原文档中"更新
@expo/metro-config至~0.22.0"与"更新babel-preset-expo至~14.0.0"采用的是 Expo 官方自有的版本号体系;而在本仓库的迁移数据中,对应版本为~54.0.9与~54.0.7(见 migrations.json 与 versions.ts 的expoV54MetroConfigVersion、babelPresetExpoV54Version常量)。迁移时以npx expo install --fix解析出的真实兼容版本为准,再与上表核对。 - versions.ts 中
minSupportedExpoVersion = '53.0.0'表明@nx/expo插件的最低支持版本即 SDK 53,而 package.json 的peerDependencies同样声明expo >= 53.0.0,说明本插件为 53→54 的迁移提供了完整支撑。
迁移后验证(Post-Migration Validation)
1. 清空所有缓存
# 清空 Expo 缓存 npx expo start --clear # 清空 Metro bundler 缓存(在项目工作区目录内执行) rm -rf node_modules/.cache/metro-* # 需要时清空 Nx 缓存 nx reset2. 逐项目运行测试
# 单独测试每个项目 nx run-many -t test -p PROJECT_NAME3. 运行全部受影响项目的测试
# 运行所有受影响项目的测试 nx affected -t test4. 在真机/模拟器上验证
# iOS nx run PROJECT_NAME:run-ios # Android nx run PROJECT_NAME:run-android5. 复核迁移检查清单
- 所有
expo-av用法已迁移到expo-audio或expo-video - 所有
expo-file-system/next导入已更新 - Android 安全区域处理已验证
- 所有图标引用已验证
- React 19.1 兼容性已验证
- Metro 与 Babel 配置已更新
- 所有测试通过
- 应用可在 iOS 模拟器/真机运行
- 应用可在 Android 模拟器/真机运行
Jest 配置的自动化迁移与手工核对
文档正文未展开,但 Nx 仓库为 SDK 54 专门实现了 Jest 相关迁移 update-jest-for-expo-54.ts,其改动恰好对应迁移后验证中的"跑测试"环节,建议迁移时一并核对:
- 移除自定义 resolver:从
jest.config.{ts,cts,js}中删除resolver属性(仅当配置同时包含jest-expopreset 和jest.resolver.js引用时)。 - 删除
jest.resolver.js文件:SDK 54 不再需要此前(update-21-4-0-add-jest-resolver引入)用于处理 Expo winter runtime 的 resolver。 - 更新
src/test-setup.ts:追加jest.mock('expo/src/winter/ImportMetaRegistry', ...)的 mock,以及global.structuredClone的 polyfill(缺失时用JSON.parse(JSON.stringify(...))兜底),且保证幂等——已存在则不重复注入,同时保留用户原有 setup 内容。 - 清理 tsconfig:从
tsconfig.app.json/tsconfig.lib.json/tsconfig.json的exclude与tsconfig.spec.json的include中移除jest.resolver.js引用。
对应测试 update-jest-for-expo-54.spec.ts 覆盖了"Expo 项目正确更新""非 Expo 项目不动""无 resolver 的 Expo 项目不动""保留已有 test-setup 内容""JS 项目同样处理""mock 不重复注入""test-setup.ts 不存在时自动创建"七类场景。手工迁移时可以对照这份清单检查自己的 Jest 配置。
常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| 组件卸载时音频播放停止 | 新版useAudioPlayerHook 会自动管理清理。如需持续播放,考虑用 Context 或状态管理方案持有播放器实例 |
| 视频播放器显示黑屏 | 确保使用useVideoPlayer+VideoView组合;检查视频源 URL 是否正确且可访问 |
| 内容被 Android 导航栏遮挡 | 用react-native-safe-area-context的SafeAreaView包裹屏幕内容,或用useSafeAreaInsets设置自定义 padding |
| 升级后图标缺失 | 对照 Expo Vector Icons 目录(icons.expo.fyi)查找被重命名/移除的图标并更新引用 |
| React 19.1 的 TypeScript 报错 | 将@types/react更新到~19.1.0,检查并移除React.FC等废弃模式 |
| Metro bundler 无法启动 | 用npx expo start --clear清空所有缓存,并确保 Metro 相关包版本兼容 |
需要审查的文件清单
用以下命令建立待审文件清单:
# 配置文件 find . -name "app.json" -o -name "app.config.*" find . -name "metro.config.*" find . -name "babel.config.*" # 包含 expo-av 导入的文件 rg "from ['\"]expo-av['\"]" --type ts --type tsx --type js # 包含 expo-file-system/next 导入的文件 rg "from ['\"]expo-file-system/next['\"]" --type ts --type tsx --type js # 包含 vector-icons 导入的文件 rg "from ['\"]@expo/vector-icons['\"]" --type ts --type tsx --type js # 安全区域相关文件 rg "SafeAreaView|useSafeAreaInsets" --type ts --type tsx --type js大型工作区的迁移策略
- 分阶段迁移:先迁移一个小项目验证流程,再逐步铺开
- 使用功能分支:为不同迁移方面创建独立分支(如
migrate/audio-video、migrate/jest),便于隔离问题与回滚 - 频繁运行测试:每次配置变更后,运行受影响的测试(
nx affected -t test) - 记录问题:持续记录项目特有的问题与解决方案,沉淀到文档
- 真机测试:Android edge-to-edge 的布局变化必须依赖真机验证
迁移期间的常用命令速查
# 找出所有 Expo 项目 nx show projects --with-target start # 启动指定项目 nx start PROJECT_NAME # 变更后测试指定项目 nx test PROJECT_NAME # 测试所有受影响项目 nx affected -t test # 查看项目详情(Web 界面) nx show project PROJECT_NAME --web # 需要时清空 Nx 缓存 nx reset # 清空 Expo 缓存 npx expo start --clear面向 LLM/AI Agent 的执行要点
当 AI Agent 执行本次迁移时,请遵循以下原则(这也是 Nx 将本文档注册为迁移 prompt 的初衷):
- 系统化推进:一次只完成一个类别,完成后再进入下一类
- 每步变更后测试:不要把所有改动批量堆叠后再统一验证
- 保持用户知情:在每个小节推进时同步进度(包括上文 Detox 场景的强制询问)
- 及时处理错误:测试失败立即修复,再继续后续步骤
- 更新文档:记录工作区特有的模式或问题
- 有意义的提交:将相关变更分组,配合清晰的信息提交(例如按类别分 commit)
- 使用 TodoWrite 工具:将迁移进度可视化追踪
- 双平台测试:Expo 的变更经常对 iOS 与 Android 产生不同影响,两端都要验证
延伸阅读:完成 SDK 54 迁移后,可参考同系列的新版迁移指引 ai-instructions-for-expo-56.md(对应 Nx 23.1.0 的迁移,涉及@expo/metro取代独立 metro-config 等变更),了解后续版本演进方向。整个迁移体系可结合 migrations.json 查看各版本迁移的注册关系,形成对@nx/expo升级机制的完整认知。
【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考