@[toc]
前言(为什么要升级)
升级成本高、第三方库老旧、原生配置冲突,这些是现实中最常见的痛点。长期不升级会导致:
-
新 iOS / Xcode / Android SDK 不被支持,编译直接报错;
-
第三方库逐渐放弃旧 API,出现运行时崩溃或无效行为;
-
无法使用新特性(Hermes 性能、Fabric 更流畅的 UI、较新的 Metro/monorepo 支持)。
React Native 官方提供了 Upgrade Helper / CLI 来辅助升级,不过从 0.63 升到 0.72 中间有很多 breaking changes,需要逐步迁移并做大量兼容性校验。(reactnative.dev)
升级前准备(Checklist:要做的清单,按优先级)
把这些当作"出门前的口袋清单"------逐项打勾再动手升级。
-
代码与依赖快照
-
git干净的主分支(或新建upgrade/0.63-to-0.72分支)。 -
保存
package.json、android/、ios/的当前快照(git tag或复制目录)。
-
-
本地环境
-
Node、Yarn/ npm 版本固定(记录当前版本)。
-
Xcode、Android SDK 的当前版本与目标版本兼容性评估。
-
-
测试矩阵准备
- 至少准备一台 iOS 设备 / 模拟器和 Android 设备 / 模拟器,记录正在运行的 RN 0.63 的行为(截图/录屏)。
-
列出关键第三方库(核心依赖)
-
RN 0.63 常见库:react-native-gesture-handler、react-native-reanimated、react-navigation、@react-native-community/async-storage 等。
-
对每个库查看维护状态、支持的 RN 版本及新版本说明。
-
-
选择升级策略
- 推荐分步升级(0.63 → 0.65 → 0.68 → 0.70 → 0.72),每步运行测试并解决冲突;或者用 Upgrade Helper 对比每个版本 diff(更安全)。Upgrade Helper 可以展示两个版本之间需要修改的文件。(react-native-community.github.io)
常见依赖冲突与解决思路(实战经验)
在升级过程中你会遇到的几类冲突,以及针对性的解决策略。
-
原生 API 被拆分 / 包名变更
-
问题:以前是
react-native的一些模块被移动到社区包(如@react-native-community/*或react-native-xxx)。 -
解决:全文搜索被移除/迁移的 API(比如
NetInfo、AsyncStorage),替换为社区包,并按 README 做 Pod / Gradle 配置调整。
-
-
CocoaPods / Podfile 冲突
-
问题:升级 RN 常常要求更新
platform :ios, 'xx.x'、或移除重复 Pod、或引入use_frameworks!的注意事项导致编译错误。 -
解决:根据 Upgrade Helper diff 调整
ios/Podfile,运行pod install --repo-update。若遇到 bitcode / arch 架构问题,先对EXCLUDED_ARCHS、VALID_ARCHS进行调试并记录变更点。
-
-
Android Gradle / Kotlin 兼容性
-
问题:RN 新版本常伴随 Gradle Plugin / AGP 要求提升。
-
解决:参考 RN release notes 调整
android/build.gradle中compileSdkVersion/targetSdkVersion/gradle plugin版本,按报错逐个升级(记住先升级 gradle wrapper,再 Gradle plugin)。
-
-
JS 层 API / Babel / Metro 配置
-
问题:Metro 配置在 0.72 做了改动,monorepo / symlink 行为可能不同。
-
解决:查看
metro.config.js与 RN 0.72 推荐配置,必要时引用 RN 的 base metro config。(reactnative.dev)
-
-
原生模块与 New Architecture(TurboModule/Fabric)适配
-
问题:New Architecture 要求自定义原生模块走 TurboModule 规范或提供兼容层,某些旧模块在开启新架构后会因为初始化/注册差异崩溃(比如
mTurboModuleRegistry hasn't been set)。(GitHub) -
解决:
-
升级第三方模块到支持 Fabric/TurboModule 的版本;
-
或者暂时不启用 New Architecture(保守策略),先保证将在旧架构下稳定运行;
-
对于自研模块,可参考官方 Migration Guide 重写为 TurboModule / Fabric。(reactnative.dev)
-
-
iOS / Android 逐项检查清单(每一项都要实际验证)
下面给出清单 + 验证方法(把这些当作"拆箱测试")。
iOS 检查
-
Pod 安装成功:
cd ios && pod install --repo-update(无警告)。 -
Xcode 编译成功(Simulator & Device):打开
.xcworkspace编译 iPhone 12 / iOS 最低支持版本。 -
确认 Info.plist 中的权限和 URL Scheme 未被覆盖。
-
Bridging Header/Header Search Paths等原生配置是否被升级时无意改动。 -
确认第三方 SDK(如 Analytics、Crashlytics)与新 iOS SDK 的兼容性。
-
确认 bitcode 设置、Flipper(如有)是否需要升级或临时禁用。
Android 检查
-
Gradle 构建成功:
./gradlew assembleDebug。 -
检查
compileSdkVersion与 NDK 配置是否符合新 RN 要求。 -
检查
AndroidManifest.xml是否有重复权限或冲突 Activity/Service。 -
检查 Proguard / R8 规则,确保混淆后的 native-lib 被保留(如果有 native code)。
-
验证多 dex、multidex 设置(老项目升级时常见)。
New Architecture(TurboModule & Fabric)带来的变化与应对(要点解读)
简短、务实地说为什么要关心它,以及应该如何处理。
-
为什么重要
Fabric 与 TurboModule(新架构)目标是性能更好、更接近原生、JS/Natives 更低延迟的交互。Fabric 改变了渲染流水线,TurboModule 用 JSI 实现更快的原生模块调用路径,对动画、手势类库有明显好处。(DEV Community)
-
迁移成本
-
第三方库需要被维护者更新(如果不更新,你要 fork 并改造)。
-
自研原生模块需要遵循新的 codegen / spec(尤其是类型定义)并用 JSI 访问。
-
-
实战建议
-
先完成版本升级,保持"旧架构"运行稳定(默认禁用 New Architecture)。
-
在 0.72 上逐步开启 New Architecture(按模块或按平台)做验证。
-
使用官方 Migration Guide 和 codegen 指南来逐步迁移自研模块。(reactnative.dev)
-
实战:从 0.63 升到 0.72 的完整流程(分步、带命令)
下面给出一个实战可复制的流程(分版本逐步升级,遇错回退点明确)。
说明:你可以把每一步都在单独的
gitcommit / tag,出现问题可回退。
- 备份与分支
bash
git checkout -b upgrade/0.63-to-0.72
git commit -am "snapshot before upgrade"
- 列出依赖并记录版本
bash
cat package.json
# 将关键依赖写入 upgrade-notes.md(例如 react-navigation, reanimated, async-storage)
- 逐步升级路径(示例)
建议路径:0.63 -> 0.65 -> 0.68 -> 0.70 -> 0.72。每步按下面操作:
以 0.63 -> 0.65 为例:
bash
# 修改 package.json 中 react-native 版本为 0.65.0
yarn add react-native@0.65.0
# 或者使用 react-native upgrade (交互会提示)
npx react-native upgrade 0.65.0
然后:
bash
cd ios && pod install --repo-update
cd android && ./gradlew clean
# iOS / Android 编译
npx react-native run-ios
npx react-native run-android
解决出现的编译错误(按照此前"依赖冲突"一节方法处理)。
-
使用 Upgrade Helper 对比代码差异
打开 Upgrade Helper(选择 from/to 版本),按它提供的文件 diff 应用必要改动(很多 config 文件、android manifest、ios pbxproj 等会变更)。(react-native-community.github.io)
-
特别注意:metro.config / monorepo / symlink
0.72 改动过 Metro 的加载方式,monorepo / symlink 项目需要检查
metro.config.js是否需要引用 base config。若你使用 monorepo,请对watchFolders和resolver做相应修改。(reactnative.dev) -
重复上述步骤直到 0.72(每次都跑完整回归测试)
可运行 Demo:最小化示例项目(package.json + 简单 TurboModule stub)
下面给出一个能跑的最小示例(重点展示如何检测原生模块兼容性与在 JS 侧降级处理)。
package.json(示例)
bash
{
"name": "rn-upgrade-demo",
"version": "0.72.0",
"private": true,
"scripts": {
"start": "react-native start",
"ios": "react-native run-ios",
"android": "react-native run-android"
},
"dependencies": {
"react": "18.2.0",
"react-native": "0.72.0",
"@react-native-async-storage/async-storage": "^1.20.1",
"react-native-gesture-handler": "^2.8.0",
"react-native-reanimated": "^3.0.0"
}
}
(具体版本要以实际库支持的最小/推荐版本为准,升级时请先去对应 repo 查说明。)
JS:降级兼容的模块加载(runtime guard)
当你怀疑某个原生模块在新架构下可能崩溃,先在 JS 侧做 guard:
bash
// NativeModuleGuard.js
import { NativeModules, Platform } from 'react-native';
export function safeRequireNativeModule(name) {
const mod = NativeModules[name];
if (!mod) {
console.warn(`[safeRequireNativeModule] ${name} not found`);
return null;
}
// 可进一步检测模块接口完整性
if (typeof mod.initialize !== 'function') {
console.warn(`[safeRequireNativeModule] ${name} missing initialize() - fallback`);
return null;
}
return mod;
}
// usage
const MyNative = safeRequireNativeModule('MyLegacyModule');
if (MyNative) {
MyNative.initialize();
} else {
// JS 替代逻辑
}
这能避免在某些初始化时机因 TurboModule 注册未就绪而直接崩溃(常见报错如 mTurboModuleRegistry hasn't been set)。(GitHub)
Android 原生:TurboModule 报错快速定位
如果开启新架构后遇到 mTurboModuleRegistry hasn't been set,先检查 MainApplication / MainActivity 中是否正确启用了新架构支持并按官方指引设置 TurboModule registry;如果不确定,临时通过 gradle.properties 关闭新架构:
bash
## gradle.properties
newArchEnabled=false
确认可以回到稳定旧架构再逐步排查。(GitHub)
典型崩溃与排查案例(真实场景风格)
下面列出常见崩溃的复现、排查和解决办法(把"理论"变成"怎么查")。
案例 A:iOS 真机崩溃,Launch 时崩溃(Crash log: EXC_BAD_ACCESS)
-
排查:
-
用 Xcode 捕获崩溃 stack,定位到哪个 native library 或 3rd SDK。
-
检查 Pod 版本、是否存在重复符号或重复依赖(某些 Pod 被多次引入)。
-
-
解决:
-
回退到上一个能正常运行的 Podfile.lock,逐个更新 Pod 并测试;
-
若为第三方 SDK 导致,联系库维护或者替换 SDK 版本。
-
案例 B:Android 运行时崩溃,日志包含 NoClassDefFoundError 或 ClassNotFoundException
-
排查:
-
检查
proguard-rules.pro是否混淆了必须保留的类。 -
检查
implementation/api依赖声明是否导致打包遗漏。
-
-
解决:
- 加入 keep 规则或调整依赖粒度。
案例 C:手势 / 动画库在 0.72 下 scroll 卡顿或崩溃
-
排查:
-
检查
react-native-reanimated与react-native-gesture-handler是否为支持新架构的版本。 -
暂时关闭 New Architecture 验证是否是 Fabric 相关兼容问题。
-
-
解决:
- 升级到对应的兼容版本或替换实现(部分场景可用纯 JS 回退实现)。
测试建议(升级后必须做的回归测试)
-
UI 回归:主流程(登录、列表、详情、文件上传、下载、离线)在 iOS/Android 真机跑至少 3 次。
-
性能回归:对比关键场景 FPS/CPU/JS 堆内存(尤其是含大量动画的页面)。
-
Crash 抓取:在升级期间保持 Crashlytics/ Sentry 的最低日志级别,观察是否新增崩溃。
-
端到端:如果有 E2E(Detox / Appium),尽量修复测试脚本并跑完整套。
升级中容易被忽视的细节(血的教训)
-
Flipper 插件版本导致的 Xcode 链接错误:升级 Flipper 或临时注释 Flipper 集成排查。
-
Metro 缓存:每次 RN 版本或依赖变动后
watchman watch-del-all && rm -rf $TMPDIR/react-* && yarn start --reset-cache。 -
旧项目有
react-native link的残留配置(手工检查android/settings.gradle、MainApplication中重复引入)。 -
monorepo 的
node_modules冲突(使用nohoist/ alias 或调整 metro resolver)。
参考 & 推荐资源(快速入口)
-
React Native 官方升级文档与 Upgrade Helper(用于对比文件 diff 并逐步修改)。(reactnative.dev)
-
React Native 0.72 发布说明(包含 Metro / 新架构互操作层等更新)。(reactnative.dev)
-
社区迁移经验与常见问题(issues / blog posts),可以作为排查灵感与补充方案。(Medium)
总结(行动建议)
-
切分阶段、逐步升级(不要一口气跳到 0.72),每一步都做完整回归测试与版本保存。
-
把关键第三方库列为"必须升级或替换"清单,优先处理对 New Architecture 兼容性的库。
-
初始目标:在旧架构下把项目跑通到 0.72,然后再考虑逐步开启 Fabric/TurboModule。(GitHub)
-
升级过程中把大改动做成独立 PR,CI 跑完再合入主分支。