React Native新架构升级实战【从 0.62 到 0.72】

一、背景

自 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 进行相应的适配与修改。具体变更内容可参考官方文档:

以下是我们在升级过程中总结的关键修改点:

1.Node版本升级

React Native 0.72 推荐使用 Node.js 16.x ~ 18.x

2.API变更

  • removeSubscriptionremoveListener 方法已被 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();
  };
}, []);
  • BackHandlerremoveEventListener 方法已被 RN 移除,替代的是 remove() 方法。
js 复制代码
import { BackHandler } from "react-native";

useEffect(() => {
  // 添加监听
  const backHandlerSub = BackHandler.addEventListener("focus", () => {});
  // 移除监听
  return () => {
    backHandlerSub.remove();
  };
}, []);
  • AppStateremoveEventListener 方法已被 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;
  }}
/>
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 不支持,会抛异常

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 的支持,升级后我们可以顺利接入最新版本库和工具,加快技术迭代节奏,减少维护成本。

相关推荐
kidding7231 分钟前
gitee新的仓库,Vscode创建新的分支详细步骤
前端·gitee·在仓库创建新的分支
听风吹等浪起4 分钟前
基于html实现的课题随机点名
前端·html
leluckys10 分钟前
flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试
前端·javascript·flutter
kidding72324 分钟前
微信小程序怎么分包步骤(包括怎么主包跳转到分包)
前端·微信小程序·前端开发·分包·wx.navigateto·subpackages
微学AI38 分钟前
详细介绍:MCP(大模型上下文协议)的架构与组件,以及MCP的开发实践
前端·人工智能·深度学习·架构·llm·mcp
liangshanbo12151 小时前
CSS 包含块
前端·css
Mitchell_C1 小时前
语义化 HTML (Semantic HTML)
前端·html
倒霉男孩1 小时前
CSS文本属性
前端·css
shoa_top1 小时前
JavaScript 数组方法总结
javascript
晚风3081 小时前
前端
前端