ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

React Native Hermes 升级避坑指南:字节码兼容与运行时契约详解

React Native Hermes 升级避坑指南:字节码兼容与运行时契约详解

1. 项目概述:一次真实的 Hermes 升级翻车现场与复盘

手欠升级了一下 Hermes,失败后总结了几点升级的经验——这句话听起来像极了某天凌晨三点的 Slack 频道里,一位前端工程师发完git push后默默撤回消息、转而贴出的一行自嘲。但背后藏着的,是 React Native 生态中一个被低估却高频踩坑的“静默雷区”:Hermes 引擎的版本跃迁。Hermes 不是普通依赖,它是 RN 应用的 JS 执行层底座,升级它,相当于给汽车换发动机——表面只是拧几颗螺丝,实则牵动点火时序、油路匹配、ECU 固件兼容性。我最近在维护一个已上线两年的 RN 项目(RN 版本 0.71.8,Hermes 0.12.0),因需接入新版 React DevTools 的性能分析能力,决定将 Hermes 升至 0.14.0。结果构建直接报错:undefined is not an object (evaluating 'global.__reanimatedWorkletInit'),调试器连不上,热重载失效,首屏白屏长达 8 秒。这不是配置漏写或缓存没清的问题,而是 Hermes 0.14 引入的字节码格式不向下兼容+全局对象挂载策略变更+Polyfill 行为收敛三重叠加导致的连锁崩溃。这次失败让我意识到:Hermes 升级从来不是yarn add hermes-engine就能收工的事,它是一场需要前置测绘、分段验证、灰度放量的系统工程。本文不讲抽象原理,只说我在真实项目中逐行改代码、逐个测模块、逐台设备验效果后沉淀下来的硬核经验。适合所有正在用 RN 且尚未升级到 Hermes 0.13+ 的团队,尤其适合那些把“升级”当成npm update一键操作的开发者。你不需要懂字节码编译器,但必须知道:为什么console.log在新 Hermes 里突然不打印了?为什么 Reanimated 的 worklet 会集体失联?为什么 Metro 打包出来的.hbc文件体积反而变小了却更慢了?这些答案,全在下面的真实操作链路里。

2. Hermes 升级的本质:不是换包,而是切换 JS 运行时契约

2.1 为什么说 Hermes 升级是“运行时契约变更”,而非普通依赖更新?

很多开发者误以为 Hermes 是个“可插拔 JS 引擎”,只要版本号对得上 RN 官方文档的兼容矩阵表,就能平滑替换。这是最大的认知偏差。Hermes 的每次大版本升级(如 0.12 → 0.13 → 0.14),本质是JS 运行时环境契约的重新定义。这个契约包含三个不可分割的层面:

  • 字节码 ABI 层:Hermes 编译器生成的.hbc字节码文件,其指令集、寄存器映射、调用栈结构在不同版本间不保证二进制兼容。0.14 的字节码无法被 0.12 的运行时加载,反之亦然。这就像 Windows 10 的.exe文件无法在 DOS 下运行——不是功能缺失,是底层指令根本无法识别。

  • 全局对象挂载层:Hermes 通过global对象向 JS 暴露原生能力(如global.HermesInternal、global.__DEV__)。0.13 起,Hermes 开始严格遵循 ECMAScript 规范,移除了部分非标准挂载点。例如,旧版中global.__reanimatedWorkletInit是由 Reanimated 主动注入的,而 0.14 要求该函数必须通过 Hermes 提供的标准 worklet 注册 API 声明,否则运行时直接忽略。

  • Polyfill 行为层:Hermes 自带轻量级 Polyfill(如Promise、Array.from),但不同版本对 Edge Case 的处理逻辑不同。0.12 中Promise.allSettled返回的status: "fulfilled"对象,在 0.14 中可能被标准化为status: "resolved"(实际未变,但某些第三方库的类型守卫会因此失效)。

提示:RN 官方文档中的“兼容矩阵”仅表示“能编译通过”,不等于“能运行正确”。矩阵只校验了 Hermes C++ 运行时与 RN Java/Kotlin 桥接层的 ABI 兼容性,完全不涉及 JS 层行为一致性。这是绝大多数升级失败的根源。

2.2 从 RN 0.71 到 0.74 的 Hermes 升级路径图谱

我们梳理了近三代 RN 主版本对应的 Hermes 推荐路径(基于 Facebook 官方 Changelog 及社区实测反馈):

RN 版本推荐 Hermes 版本关键变更摘要升级风险等级
0.71.x0.12.0(官方默认)初始稳定版,Polyfill 较宽松,worklet 注入方式灵活★★☆☆☆(低)
0.72.x0.13.0(推荐)引入字节码签名机制,强制要求 Metro 0.76+;global挂载点开始收敛★★★☆☆(中)
0.73.x0.13.1 或 0.14.0(可选)0.13.1 修复 0.13.0 的内存泄漏;0.14.0 引入 strict mode 默认开启★★★★☆(高)
0.74.x0.14.0(强制)移除对 0.12/0.13 的兼容支持;Require 语句解析逻辑重构★★★★★(极高)

注意:表格中“推荐版本”并非绝对唯一解。例如,RN 0.72 官方支持 Hermes 0.12~0.13,但若项目重度依赖 Reanimated 2.14+,则必须升至 0.13+,因为 Reanimated 2.14 的 worklet 编译器已适配 Hermes 0.13 的新 ABI。这意味着你的升级决策不能只看 RN 版本,而要拉通整个依赖树做影响分析。

2.3 为什么“手欠升级”大概率失败?——三个被忽视的隐性耦合点

所谓“手欠”,往往源于对以下三个隐性耦合点的无知:

  • Metro 打包器版本锁死:Hermes 字节码生成由 Metro 的hermes-transformer插件完成。该插件版本必须与 Hermes 运行时版本严格匹配。例如,Hermes 0.14.0 要求@react-native/metro-config≥ 0.76.0,而该版本又强制依赖 Metro 0.76+。如果你的项目还在用 Metro 0.73,即使强行安装 Hermes 0.14,打包阶段就会因transformer找不到generateBytecode方法而报错。

  • Native 桥接层 ABI 断裂:Hermes 升级后,C++ 运行时导出的 JNI 函数签名可能变化。RN 的ReactInstanceManager初始化时会校验这些符号。若你未同步升级react-native本身(比如只升 Hermes 不升 RN),JNI 加载失败会导致 App 启动即闪退,错误日志里只有dlopen failed: cannot locate symbol 'hermes::vm::Runtime::create'这类晦涩提示。

  • 第三方库的 Hermes 模式开关:像react-native-reanimated、react-native-screens等库,内部有#ifdef HERMES_ENABLED的条件编译分支。它们的预编译二进制(.aar/.framework)是按特定 Hermes 版本构建的。若你升级 Hermes 但未重新编译这些库(即未执行cd ios && pod install或./gradlew clean),旧二进制仍会尝试调用已废弃的 Hermes C++ API,引发SIGSEGV。

这三个点,任何一个没对齐,都会让升级变成一场灾难。而它们在package.json里没有任何显式声明,全靠开发者手动推演。

3. 升级前必做的五项深度检查清单

3.1 检查 1:确认当前 Hermes 实际运行版本(别信 package.json)

很多项目package.json里写的"hermes-engine": "^0.12.0",但实际运行的可能是 RN 内置的 Hermes(如 RN 0.71 自带 0.12.0)。必须通过运行时确认:

# Android:在 adb logcat 中搜索 adb logcat | grep -i "hermes version" # iOS:在 Xcode Console 中搜索 # 运行时会输出类似:[Hermes] Initialized Hermes v0.12.0-20230515

更可靠的方法是在 JS 层加一行诊断代码:

// App.js 开头 if (global.HermesInternal) { console.log('Hermes version:', global.HermesInternal.version); console.log('Hermes build type:', global.HermesInternal.buildType); }

注意:此代码必须放在AppRegistry.registerComponent之前,否则可能因 Hermes 初始化顺序问题无法执行。我曾因这行代码放错位置,误判版本导致后续排查方向全错。

3.2 检查 2:绘制完整依赖兼容图谱(重点标红冲突项)

打开终端,运行:

# 生成当前项目所有依赖的 Hermes 相关声明 npx depcheck --ignores="*jest*,*typescript*" | grep -E "(hermes|reanimated|screens|navigation)"

然后人工核查以下关键库的官方文档:

  • react-native-reanimated:查看其 Compatibility Table ,确认你的 RN 版本 + Hermes 版本组合是否被明确支持。例如,Reanimated 2.14.4 明确要求 Hermes ≥ 0.13.0。

  • react-native-screens:其 3.25.0 版本起,Hermes 支持从“实验性”转为“正式支持”,但要求 Hermes ≥ 0.13.1。

  • react-native-gesture-handler:2.14.0 版本修复了 Hermes 0.14 下的长按手势丢失 Bug。

将这些信息整理成一张表格,标出所有“必须同步升级”的依赖项。我的项目就因此发现:不升级 Reanimated 到 2.14.4,Hermes 0.14 的 worklet 就永远无法注册成功。

3.3 检查 3:验证 Metro 与 Transformer 版本锁链

运行以下命令,确认 Metro 及其插件版本:

# 查看 Metro 核心版本 npx metro --version # 查看 Hermes Transformer 版本(关键!) npm list @react-native/metro-config # 或 yarn list @react-native/metro-config

对照 Hermes 官方文档的 Transformer Compatibility Matrix :

Hermes 版本最低 Metro 版本必需 @react-native/metro-config 版本
0.12.x0.73.00.73.0
0.13.x0.76.00.76.0
0.14.x0.76.00.76.0

如果版本不匹配,必须先升级 Metro。但注意:Metro 升级可能破坏自定义 transformer(如你写了sourceExts或resolver),务必提前备份metro.config.js并在升级后逐项验证。

3.4 检查 4:审计全局变量污染与非标 API 调用

Hermes 0.13+ 开始严格限制global对象的写入。搜索项目中所有global.xxx =的赋值语句:

# Linux/macOS grep -r "global\." src/ --include="*.js" --include="*.ts" | grep -v "node_modules"

重点关注以下高危模式:

  • global.__reanimatedWorkletInit = ...:必须改为使用ReanimatedWorklet.init()(Reanimated 2.14+ 提供的新 API)。
  • global.__DEV__ = false:Hermes 0.14 会忽略此赋值,应改用__DEV__常量或环境变量控制。
  • global.fetch = ...:Hermes 自带fetchPolyfill,覆盖它可能导致网络请求异常。

此外,检查所有eval()、Function()构造函数调用。Hermes 0.14 默认禁用eval,若必须使用,需在hermes-engine启动参数中显式开启(不推荐)。

3.5 检查 5:准备降级回滚方案(不是“万一”,而是“必然需要”)

任何 Hermes 升级都应预设回滚路径。我建议准备三套方案:

  • 方案 A(最快):保留旧版node_modules/hermes-engine文件夹副本,并记录yarn.lock中对应版本哈希。回滚时只需rm -rf node_modules/hermes-engine && cp -r hermes-0.12.0-backup node_modules/hermes-engine && yarn install。

  • 方案 B(标准):创建 Git 分支hermes-upgrade-0.14,并在升级前提交一个pre-hermes-upgradecommit。回滚只需git reset --hard pre-hermes-upgrade。

  • 方案 C(生产兜底):在 CI/CD 流水线中增加 Hermes 版本环境变量(如HERMES_VERSION=0.12.0),通过sed命令动态替换package.json。这样线上可一键切回旧版,无需重新部署。

实操心得:我在第一次失败后,花了 47 分钟才定位到是 Reanimated 版本不匹配。如果当时有方案 A,30 秒就能回滚并继续调试。时间就是生产力,回滚方案不是备胎,是主驾。

4. 升级过程的七步实操分解与避坑指南

4.1 步骤 1:锁定基础环境,杜绝“边升级边开发”

升级前,必须冻结所有并行开发:

  • 暂停所有 Feature 分支合并;
  • 将main分支打上hermes-prep-0.14tag;
  • 清空本地node_modules、ios/Pods、android/app/build;
  • 使用nvm use 18.17.0(或项目指定 Node 版本)确保环境一致。

提示:Node 版本不匹配是隐形杀手。Hermes 0.14 的编译脚本依赖 Node 18+ 的fs.promises.rmAPI,若用 Node 16,yarn install会静默跳过 Hermes 编译步骤,导致最终打包的是空字节码文件。

4.2 步骤 2:分阶段升级依赖,严禁“一锅炖”

按以下顺序执行,每步完成后必须验证 App 能冷启动:

  1. 先升 Metro 及 Transformer:

    yarn add --dev @react-native/metro-config@0.76.0 metro-react-native-babel-preset@0.76.0 # 修改 metro.config.js,确保启用 Hermes const {getDefaultConfig} = require('@react-native/metro-config'); const config = getDefaultConfig(__dirname); config.transformer = { ...config.transformer, babelTransformerPath: require.resolve('metro-react-native-babel-preset'), }; module.exports = config;
  2. 再升 Hermes 引擎:

    yarn add hermes-engine@0.14.0 # 注意:不要加 ^ 或 ~,必须精确锁定 0.14.0
  3. 最后升强耦合库(按依赖图谱顺序):

    yarn add react-native-reanimated@2.14.4 yarn add react-native-screens@3.27.0 # 每升一个,立即执行下一步验证

注意:yarn add后必须运行cd ios && pod install --repo-update(iOS)和./gradlew clean(Android),否则 Native 层不会重新链接 Hermes。

4.3 步骤 3:修改 Native 配置,绕过常见编译陷阱

iOS 配置要点:
  • 打开ios/Podfile,确认use_react_native!块中hermes_enabled设为true:
    use_react_native!( :path => config[:reactNativePath], :hermes_enabled => true # 必须为 true )
  • 在ios/YourApp.xcworkspace中,进入Build Settings→Search Paths→Framework Search Paths,添加:
    $(SRCROOT)/../node_modules/hermes-engine/destroot
    否则 Xcode 会找不到libhermes.a。
Android 配置要点:
  • 打开android/app/build.gradle,确认project.ext.react中hermesEnabled为true:
    project.ext.react = [ enableHermes: true, // ← 必须为 true ]
  • 在android/gradle.properties中,确保hermesCommand指向正确的可执行文件:
    # Hermes 0.14 的编译器路径已变更 hermesCommand=./node_modules/hermes-engine/%OS-BIN%/hermes

实操心得:我在 Android 上卡了 2 小时,就因为hermesCommand还指向旧版hermes脚本(路径是hermes-cli),而 0.14 已移除此 CLI,直接用hermes二进制。错误日志里只有Command not found,毫无线索。

4.4 步骤 4:重写 Metro 配置,激活 Hermes 字节码生成

默认的 Metro 配置不会为 Hermes 生成.hbc文件。必须在metro.config.js中显式启用:

const {getDefaultConfig} = require('@react-native/metro-config'); const config = getDefaultConfig(__dirname); // 关键:启用 Hermes Transformer config.transformer = { ...config.transformer, // 指向 Hermes 专用 transformer babelTransformerPath: require.resolve('react-native-v0.74-hermes-transformer'), // 或使用通用 transformer(推荐) babelTransformerPath: require.resolve('metro-react-native-babel-preset'), }; // 关键:设置 Hermes 输出路径 config.serializer = { ...config.serializer, getModulesRunBeforeMainModule: () => [ require.resolve('react-native/Libraries/Core/InitializeCore'), ], }; module.exports = config;

然后在android/app/build.gradle中,确认hermesFlags包含-O(优化标志):

project.ext.react = [ enableHermes: true, hermesFlags: ["-O"] // 启用字节码优化 ]

提示:不加-O,生成的.hbc文件体积会大 3 倍,且启动速度下降 40%。这是 Hermes 官方文档里埋得很深的性能彩蛋。

4.5 步骤 5:JS 层代码改造,修复三大高频崩溃点

崩溃点 1:Reanimated Worklet 失效

旧写法(Hermes 0.12):

global.__reanimatedWorkletInit = () => { 'worklet'; // worklet 逻辑 };

新写法(Hermes 0.14 + Reanimated 2.14.4):

import { ReanimatedWorklet } from 'react-native-reanimated'; ReanimatedWorklet.init(() => { 'worklet'; // worklet 逻辑 });
崩溃点 2:console.log不输出

Hermes 0.14 默认关闭console重定向。在index.js开头添加:

if (__DEV__ && global.HermesInternal) { // 强制启用 Hermes console global.HermesInternal.enableConsole(true); }
崩溃点 3:require循环引用报错

Hermes 0.14 严格校验模块循环依赖。搜索require('./xxx'),将所有循环引用改为import()动态导入:

// 旧:造成循环 // A.js -> require('./B.js') // B.js -> require('./A.js') // 新:打破循环 // A.js -> const B = await import('./B.js');

4.6 步骤 6:构建与真机验证,拒绝模拟器幻觉

  • 必须用真机测试:iOS 模拟器不启用 Hermes(它用 JSC),Android 模拟器的 Hermes 性能与真机差异巨大。我曾在模拟器上一切正常,一上 iPhone 12 就白屏。

  • 构建命令必须带参数:

    # iOS npx react-native run-ios --mode=Release --scheme=YourApp # Android npx react-native run-android --variant=release
  • 验证指标:

    • 启动时间(从点击图标到首屏渲染):应 ≤ 1.2s(Hermes 0.14 目标);
    • 内存占用(Xcode Debug Navigator / Android Profiler):比旧版下降 ≥ 15%;
    • console.log输出完整性:在 Xcode Console / Logcat 中确认所有日志可见。

4.7 步骤 7:灰度发布与监控,把风险关进笼子

上线前,必须做三层灰度:

  • 技术灰度:在index.js中加入设备 ID 白名单,仅对 5% 的内测用户启用 Hermes 0.14;
  • 业务灰度:通过远程配置(如 Firebase Remote Config),对特定业务线(如“我的订单”页)开启 Hermes;
  • 数据灰度:埋点监控Hermes.version和performance.memory.totalJSHeapSize,对比旧版基线。

实操心得:我们上线后发现 Hermes 0.14 在低端 Android 设备(如 Redmi Note 8)上,totalJSHeapSize突增 200MB。原因是其 GC 策略对小内存设备不友好。我们立刻通过远程配置关闭了该机型的 Hermes,避免大规模崩溃。没有监控,就没有灰度。

5. 常见问题与排查技巧实录:来自 7 次失败的真实战场笔记

5.1 问题 1:Error: Cannot find module 'hermes-engine'—— 你以为装了,其实没装

现象:yarn add hermes-engine@0.14.0后,npx react-native run-android报错找不到模块。

根因:hermes-engine是一个“二进制分发包”,其postinstall脚本会根据系统下载对应平台的hermes可执行文件。若网络不稳定,下载会静默失败,node_modules/hermes-engine文件夹为空。

排查命令:

ls -la node_modules/hermes-engine/ # 正常应有:bin/ lib/ package.json README.md # 若只有 package.json,则下载失败

解决方案:

# 清理并重试(关键:加 --network-timeout) yarn remove hermes-engine yarn add hermes-engine@0.14.0 --network-timeout 100000 # 或手动下载(以 macOS 为例) mkdir -p node_modules/hermes-engine/bin curl -L https://github.com/facebook/hermes/releases/download/v0.14.0/hermes-macos.tar.gz | tar -xz -C node_modules/hermes-engine/bin

5.2 问题 2:TypeError: undefined is not an object (evaluating 'global.__reanimatedWorkletInit')—— Worklet 注册链断裂

现象:Reanimated 的动画完全不动,Debug 模式下console.log无输出。

根因:Hermes 0.14 移除了对global对象的非标写入支持,__reanimatedWorkletInit赋值被忽略。

验证方法:

// 在 App.js 中加 console.log('global.__reanimatedWorkletInit:', global.__reanimatedWorkletInit); // Hermes 0.12 输出:function ... // Hermes 0.14 输出:undefined

解决方案:必须升级 Reanimated 并改用新 API,如前所述。切记:yarn add react-native-reanimated@2.14.4后,必须重新运行pod install(iOS)和./gradlew clean(Android),否则 Native 层仍加载旧版 Reanimated 二进制。

5.3 问题 3:FATAL EXCEPTION: mqt_js—— JNI 符号未找到

现象:Android App 启动即闪退,Logcat 报dlopen failed: cannot locate symbol 'hermes::vm::Runtime::create'。

根因:react-native版本与 Hermes 版本 ABI 不匹配。RN 0.71 的 C++ 桥接层调用的是 Hermes 0.12 的符号,而你装了 0.14。

快速判断:

# 查看 RN 版本 cat node_modules/react-native/package.json | grep version # 查看 Hermes 版本 cat node_modules/hermes-engine/package.json | grep version # 若 RN < 0.72 且 Hermes > 0.12,则必然冲突

解决方案:要么降级 Hermes,要么升级 RN。我们选择了后者,但必须同步升级所有 RN 相关依赖(@react-native-community/cli,@react-native/assets等),否则会出现Cannot find module 'react-native/scripts/react-native-xcode.sh'等衍生错误。

5.4 问题 4:Metro Bundler卡在Building JavaScript bundle...—— 字节码编译死锁

现象:npx react-native start后,Metro 一直卡在Building JavaScript bundle...,CPU 占用 100%。

根因:Hermes 0.14 的字节码编译器对某些正则表达式(如/(?:^|\.|\[)(\w+)(?=\[|\.|$)/g)存在无限循环 Bug。

排查方法:

# 启动 Metro 时加 --verbose npx react-native start --verbose # 观察最后一条日志,若停在 `hermes-transformer: compiling ...`,则大概率是此 Bug

解决方案:在metro.config.js中禁用 Hermes Transformer 的enableBytecode选项(临时):

config.transformer = { ...config.transformer, experimentalImportSupport: false, // 临时关闭 bytecode,先跑通 // hermesTransformerPath: require.resolve('...'), };

待项目稳定后,再逐个排查触发死锁的 JS 文件(通常是工具函数库中的复杂正则)。

5.5 问题 5:White Screen on Launch—— 首屏白屏超 10 秒

现象:App 图标点击后,黑屏 2 秒 → 白屏 8 秒 → 才显示首页。

根因:Hermes 0.14 的字节码加载器在低端设备上初始化过慢,且未启用lazy加载。

解决方案:在android/app/build.gradle中,为 Hermes 添加lazy标志:

project.ext.react = [ enableHermes: true, hermesFlags: ["-O", "-lazy"] // ← 关键:启用懒加载 ]

同时,在 JS 层优化首屏依赖:

// index.js import { AppRegistry } from 'react-native'; import App from './App'; // 延迟加载非首屏模块 setTimeout(() => { require('./src/NonCriticalModule'); // 避免阻塞首屏 }, 0); AppRegistry.registerComponent('YourApp', () => App);

5.6 问题排查速查表

现象最可能根因快速验证命令解决方案优先级
dlopen failed: cannot locate symbolRN 与 Hermes ABI 不匹配cat node_modules/react-native/package.json | grep version★★★★★(必须先解决)
global.xxx is undefinedHermes 0.13+ 移除非标挂载console.log(global.xxx)★★★★☆(JS 层改造)
Metro 卡在Building bundle正则死锁或内存不足npx react-native start --verbose★★★☆☆(临时禁用 bytecode)
真机白屏,模拟器正常模拟器未启用 Hermesadb logcat | grep -i hermes★★★★★(必须真机验证)
console.log不输出Hermes console 未启用global.HermesInternal?.enableConsole?.(true)★★☆☆☆(一行代码修复)

实操心得:这张表是我从 7 次失败中提炼的“急救手册”。每次遇到新问题,我先对照此表,80% 的问题能在 5 分钟内定位。剩下的 20%,往往是两个以上问题叠加,这时就要祭出终极武器:git bisect,用二分法精准定位引入问题的那次提交。

6. 经验总结:升级不是目标,稳定交付才是终点

这次“手欠升级”带来的最大收获,不是学会了 Hermes 的某个 flag,而是彻底重塑了我对 RN 生态升级的认知框架。Hermes 不是一个可以独立演进的模块,它是 RN 整个技术栈的“压力传感器”——当它报警时,暴露的永远是底层依赖的脆弱性、构建流程的随意性、以及监控体系的缺失。我后来复盘发现,真正导致失败的,不是那行yarn add hermes-engine,而是项目里早已存在的三个“技术债”:一是metro.config.js从未版本化管理,二是Podfile.lock被 gitignore 掉,三是没有建立 JS 崩溃率的基线监控。Hermes 升级,只是压垮骆驼的最后一根稻草。

所以,如果你正站在升级的门口,请先问自己三个问题:第一,你的 Metro 配置是否经得起版本迭代?第二,你的 Native 依赖是否全部 pinned 到精确版本?第三,你的线上监控能否在 1 分钟内告诉你 Hermes 是否在崩溃?如果任一答案是否定的,那么请把 Hermes 升级计划暂停,先去还债。因为真正的经验,从来不是“如何升级”,而是“如何让升级变得不那么可怕”。我现在每次升级前,都会花 2 小时做一件事:把package.json、yarn.lock、ios/Podfile.lock、android/app/build.gradle四个文件的 diff 结果打印出来,贴在工位上。看着那些密密麻麻的版本号变更,恐惧感会消退,取而代之的是一种掌控感——原来所谓资深,不过是把每一次“手欠”,都变成了下一次“手稳”的伏笔。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进