一、背景
自 2020 年起,为了提升开发效率,我们在原生项目中引入了当时最新的 React Native 0.62,实现了跨端统一开发。随着业务不断扩展,至今已累计构建 200+ 个 RN 模块,覆盖千万级用户,并完善了 UI 组件库、脚手架、性能与错误监控、业务库等基础设施。然而,基于 React Native 0.62 的架构已逐步显现性能瓶颈与可维护性限制,难以满足我们对应用体验和渲染效率的日益增长的需求。为确保整个平台的可持续演进,并为未来新功能和技术升级夯实基础,我们决定将 React Native 升级至 0.72 版本。

本次升级的主要驱动因素包括以下几点:
1.显著的性能提升
React Native 0.72 对新架构(Fabric 渲染器 + TurboModules)提供了更完整的支持,带来多项关键优化:
- 渲染更流畅:Fabric 通过 C++ 重构渲染路径,显著提升 UI 响应速度与交互流畅度。
- 通信更高效:TurboModules 提高了 JS 与原生模块之间的调用效率,有效降低通信延迟。
- 资源管理更精细:模块按需加载机制优化了资源利用,避免了不必要的性能开销。
2. 更好的系统兼容性
0.72 版本对 Android 13 及 iOS 16/17 提供了更强适配能力,增强了系统稳定性、安全性和兼容性。相比之下,旧版本在新系统上可能会出现警告、崩溃或 UI 异常等问题。
3. 更完善的社区生态与三方库支持
随着社区快速发展,许多主流第三方库已不再支持 0.62 版本,转而基于 0.70+ 进行迭代。通过升级,我们能够更好地对接活跃的开源生态,提升整体开发效率与模块稳定性。
4. 更好的调试体验与开发工具链
RN 0.72 默认集成新版 Hermes 引擎、增强版 Metro 构建器、性能分析工具以及全新 DevTools,带来更高效的调试体验,尤其在处理大型业务页面时,构建速度与调试性能均有显著提升。
二、升级挑战
由于 React Native 一直未发布正式版本,每次版本更新都有可能是颠覆性的,包括 API 的废弃与新增,并没有对低版本较好的兼容性,因此也会引发旧模块或第三方库的不兼容问题。考虑到我们当前已集成超过 200+ RN 模块,直接整体迁移至新版本工作量巨大,并且升级过程中难以避免潜在的 Bug 或异常。
因此,为了降低版本迁移带来的系统性风险,我们计划采用渐进式升级策略,在保障业务稳定性的前提下,逐步完成对所有新版本的过渡。
三、新架构适配
在和原生团队一起阅读了大量 RN 版本变更记录后,我们推测:低版本的 RN 包有可能兼容运行于新版 Hermes 引擎之上。这一判断为我们制定灰度升级方案提供了关键前提。
基于该假设,我们首先由原生团队将引擎升级至新架构,构建出自验版本的 App 包,而 RN 侧保持不变。通过这个自验包,我们对线上运行的旧版 RN 模块进行了初步验证,仅用较短时间即完成全量功能的跑通测试。
随后,测试团队进一步介入,针对各个 RN 模块展开更细粒度的验证。最终验证结果表明:React Native 0.62 版本的模块可以稳定运行在新版 Hermes 引擎之上。
四、平滑的升级策略
基于上述验证结果,为确保升级过程中的 App 稳定性,我们对各模块按重要等级进行分级,采用由低到高、分批次升级的策略。
升级期间,App 同时支持运行 RN 0.62 和 0.72 两个版本,通过业务包中的版本标识动态选择加载对应版本的RN。具体实现方式如下:
-
基础包拆分 :在 RN 模块的打包过程中,我们通过构建脚本将 RN 的核心依赖与业务代码进行拆分,分别生成两个独立的 JS Bundle。
基础包
:包含 RN 核心框架及公共依赖,所有模块共享一份,预埋在 App 包中。业务包
:仅包含对应模块的业务代码,支持热更新。每次发布后,业务包通过线上下载到本地,与基础包合并运行。 -
基础包准备:为兼容 RN 0.62 和 0.72 两个版本,我们基于各自的依赖和拆包方案,分别构建对应的两个版本基础包,并同时预置集成在 App 中。
-
业务包版本标识 :在项目的
package.json
文件中新增 RN 版本号字段,并在打包过程中将该版本信息注入到业务包的 JS Bundle 中,作为识别依据。 -
全量包合并机制:App 在下载业务包后,根据其中的版本标识动态选择加载对应的基础包(0.62 或 0.72),并在本地完成合并,形成可运行的全量包。该机制支持业务模块灵活切换基础运行环境。
-
模块渐进式升级:借助上述机制,我们无需一次性升级所有 RN 模块,而是可以按需、逐步迁移,最大限度降低升级风险,同时便于持续发现和解决潜在问题。
五、前端代码适配
React Native 升级不仅涉及原生端 SDK 的更新,前端也需根据新版 API 进行相应的适配与修改。具体变更内容可参考官方文档:
-
RN官方升级助手 :github.com/react-nativ...
-
RN升级问题支持 :github.com/react-nativ...
-
RN新架构讨论 :github.com/reactwg/rea...
以下是我们在升级过程中总结的关键修改点:
1.Node版本升级
React Native 0.72 推荐使用 Node.js 16.x ~ 18.x
2.API变更
removeSubscription
和removeListener
方法已被 RN 移除,替代的是remove()
方法。
js
import { DeviceEventEmitter, NativeModules, NativeEventEmitter } from 'react-native';
// 自定义原生模块
const { SampleModule } = NativeModules;
const nativeEmitter = new NativeEventEmitter(SampleModule);
useEffect(() => {
// JS 事件监听
const jsEventSub = DeviceEventEmitter.addListener('eventName', (data) => {});
// 自定义原生事件监听
const nativeEventSub = nativeEmitter.addListener('eventName', (data) => {});
// 清理除监听
return () => {
jsEventSub.remove();
nativeEventSub.remove();
};
}, []);
BackHandler
的removeEventListener
方法已被 RN 移除,替代的是remove()
方法。
js
import { BackHandler } from "react-native";
useEffect(() => {
// 添加监听
const backHandlerSub = BackHandler.addEventListener("focus", () => {});
// 移除监听
return () => {
backHandlerSub.remove();
};
}, []);
AppState
的removeEventListener
方法已被 RN 移除,替代的是remove()
方法。
js
import { AppState } from "react-native";
useEffect(() => {
// 添加监听
const appStateSub = AppState.addEventListener("change", () => {});
// 移除监听
return () => {
appStateSub.remove();
};
}, []);
navigation
的清除订阅方式变更,订阅对象会直接返回清除函数。
js
import { useNavigation } from "@react-navigation/native";
const navigation = useNavigation();
useEffect(() => {
// 添加监听
const focusSub = navigation.addListener("focus", () => {});
// 移除监听
return () => {
focusSub();
};
}, [navigation]);
Image
组件的onLoad
事件对象source.url
现在已重命名为source.uri
。
js
<Image
onLoad={(event) => {
// 注意:在 RN 0.70+ 之后使用的是 uri,而不是 url
const imageUri = event.nativeEvent.source.uri;
}}
/>
- 正则不支持命名捕获组,如
(?<Name>x)
。参考:github.com/facebook/he...
js
// 不兼容 Hermes 的写法
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const date = "2025-04-22";
const match = regex.exec(date);
console.log(match?.groups?.year); // ❌ Hermes 不支持,会抛异常
- Hermes引擎不支持非ISO8601规范的时间语法,如
Date('2023/3/30')
。参考:juejin.cn/post/746967...
3.API移除
React Native 0.72 中,以下组件已从原库中移除,需迁移至对应的新库:
AsyncStorage
→ 使用@react-native-async-storage/async-storage
VirtualizedList
→ 使用react-native/virtualized-lists
Slider
→ 使用@react-native-community/slider
DatePickerIOS
→ 使用@react-native-community/datetimepicker
ProgressViewIOS
→ 使用@react-native-community/progress-view
react-native-gradle-plugin
→ 使用react-native/gradle-plugin
react-native-codegen
→ 使用@react-native/codegen
@react-native-community/picker
→ 使用@react-native-picker/picker
@react-native-community/eslint-plugin
→ 使用@react-native/eslint-plugin
@react-native-community/eslint-config
→ 使用@react-native/eslint-config
4.依赖升级
必须升级的核心库,其他三方库也要配套升级。
json
{
"react": "18.2.0",
"react-native": "0.72.5",
"@react-native-picker/picker": "2.6.1",
"@react-native/metro-config": "0.72.11",
"@react-navigation/bottom-tabs": "6.6.1",
"@react-navigation/native": "6.1.18",
"@react-navigation/stack": "6.4.1",
"@react-navigation/material-bottom-tabs": "6.2.29",
"react-test-renderer": "18.2.0",
"babel-jest": "29.2.1",
"jest": "29.2.1",
"@types/react": "18.0.24",
"@tsconfig/react-native": "3.0.0",
"typescript": "4.8.4",
"@react-native/eslint-config": "0.72.0",
"@typescript-eslint/eslint-plugin": "5.0.0",
"@typescript-eslint/parser": "5.0.0",
"eslint": "8.19.0"
}
5.metro.config.js
js
const { mergeConfig, getDefaultConfig } = require("@react-native/metro-config");
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
6.tsconfig.json
json
{
"extends": "@tsconfig/react-native/tsconfig.json" // 修改点
}
7.jest.config.js
js
module.exports = {
preset: "react-native",
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!(@react-native)', // 增加@react-native
]
};
8.eslintrc.js
js
module.exports = {
extends: "@react-native", // 修改点
};
9.react-native-echarts-pro 图表库空白问题解决
- react-native-echarts-pro升级至1.9.3
- 图表配置的formatter不支持传入函数,要在函数转为字符串
- formatter函数中不能写注释
- formatter中不能写TypeScript
- 函数内每一行必须分号结束
- RNEChartsPro组件传递enableParseStringFunction为true
- 嵌套字符串必须采用外层双引号,内层单引号的标准格式
参考:
js
// 格式化函数
formatter: `(params) => {
'show source';
const data = params[0];
const marker = "<span style='margin-right:4px;width:9px;height:9px;background-color:" + data.color + ";border-radius:2px;display:inline-block;'></span>";
const xName = "<span style='margin-right:8px;'>" + data.name + '</span>';
const yValue = '<span>' + data.value + '</span>';
const content = "<span style='display:flex; align-items:center;'>" + marker + xName + yValue + '</span>';
return '<span><span>' + data.seriesName + '</span>' + content + '</span>';
}`,
// 组件传参
<RNEChartsPro
enableParseStringFunction
/>
六、总结
本次 React Native 从 0.62 升级到 0.72 的过程,虽然面临一定的技术挑战,但通过引入"双基础包共存"机制,实现了对新旧版本模块的并存支持,让升级过程变得更加可控和柔性。
在这一过程中,我们收获了多个层面的提升:
✅ 技术架构升级准备就绪
借助 0.72 对新架构的支持,我们完成了底层兼容性打通,为后续启用 Fabric 渲染器、TurboModules 等关键技术做了技术铺垫。
🚀 性能表现明显优化
新版 Hermes 引擎、精简后的依赖树、更快的构建和渲染速度,帮助我们提升了页面启动速度和 UI 响应能力,为用户体验加分。
🔧 开发体验大幅提升
新的调试工具、构建系统和包管理逻辑更加现代化,让开发调试效率得到显著提高,同时减少了因旧版库兼容性差导致的问题。
🔄 模块升级节奏可控
通过业务包标识和双基础包的机制,我们可以按模块逐步升级,不影响现网用户的稳定性,极大降低了升级风险。
📦 更好的生态兼容性
社区主流库基本已放弃对 0.62 的支持,升级后我们可以顺利接入最新版本库和工具,加快技术迭代节奏,减少维护成本。