Hermes 这个移动端 JavaScript 引擎,我前后用了三个月。刚开始和大多数人一样,只听说它能加快 App 启动、降低内存占用,就打开文档一通照做。等真把项目切过去之后才意识到,光会打开开关没用,真正要花时间理解的是另外几件事:字节码是怎么参与运行的,内存问题怎么查,调试器怎么连,还有工程上怎么安全落地。这四件事如果没弄明白,后面每一个坑都会变成弯路上的回填土。这篇文章就把我三个月里觉得最值得优先学的东西整理出来,也顺带记一下踩过的真坑。
1. 先搞懂 Hermes 是什么,再决定从哪学起
很多教程一上来就教你怎么开启 Hermes、怎么配置构建参数,但我建议你先退一步,搞清楚这个引擎到底解决的是哪一类问题。不然你只会"打开了开关",出了问题依然一脸懵。
1.1 它解决的三个实际问题
移动端的运行环境比桌面端苛刻得多。手机的物理内存看着有 6GB、8GB,但单个 App 能从系统手里拿到的堆内存非常有限,尤其低端 Android 机,分给你的内存可能只有 200 到 300MB。用户从点图标到看到首帧画面的时间窗口,通常也就几百毫秒,这段时间里引擎要去读源码、做语法分析、生成可执行代码、再启动业务模块,任何一个环节拖慢,体感就是白屏或者卡顿。
浏览器引擎那套"先把 JS 源码边解析边执行,执行热了再交给 JIT 做优化"的打法,在桌面端和服务器上没问题,搬到移动端就是一场灾难。启动慢是一方面,频繁的垃圾回收还会抢占主线程,导致列表滑动时一卡一卡的。Hermes 的做法很直接:在构建阶段就提前把 JS 源码编译成字节码,打包进 App。用户执行到这段代码时,不再需要从源码开始现场解析,而是直接加载已经编译好的字节码,交给解释器运行。启动阶段的解析成本被彻底前置到了开发者构建的那一次操作里。
打个比方,这就像点外卖。商家提前把菜洗好切好,你下单之后直接开火炒,十来分钟就能出餐。传统模式是顾客下单之后厨师才去菜市场买菜、洗菜,时间全耗在准备环节。Hermes 就是把"备菜"这一步提前做完,把省下来的时间留给你真正想跑的菜品逻辑。对应到 App 上,就是首屏时间更短、交互响应更稳定。
1.2 它和 JSC、V8 到底有什么不同
做跨端开发的人大多接触过 JSC、V8 这类的浏览器衍生引擎。它们在设计目标上就不是一回事。JSC 和 V8 追求的是"极端情况下的峰值计算性能",尽量减少解释执行的开销,把热代码通过 JIT 编译成本地指令。Hermes 的第一优先级是"在低内存环境下稳定跑",所以它在启动延迟、内存占用量、代码体积之间做了一组取舍。
| 维度 | 偏向 Hermes | 偏向 JSC / V8 类通用 JS 引擎 |
|---|---|---|
| 代码处理策略 | 构建期预编译为字节码,运行时以解释执行为主 | 运行时解析源码,热点代码再触发 JIT 编译 |
| 启动敏感度 | 高度敏感,优先压低首帧时间 | 更看重长时稳定吞吐 |
| 内存控制 | 内存管理按移动端场景调优,GC 优先放后台 | 内存策略偏通用,桌面和服务器场景更强 |
| 适用场景 | 移动 App、嵌入式、资源受限设备 | 浏览器、服务端、复杂计算 |
这并不意味着 Hermes 在纯 JS 计算速度上比 JSC 快,实际上在复杂算法这种重 CPU 的任务上,它反而可能吃亏。工程上更关心的是:绝大多数业务代码根本不到拼计算峰值的程度,大家体感最强烈的还是冷启动时间、内存吃不吃紧、低端机崩不崩。想清楚这一点,你就不会再犯"照着一篇性能评测文章就盲目换引擎"的错。
1.3 它适合谁来学
我认为四类人值得认真学:
第一类是移动端跨端项目开发者,尤其是项目里还有不少低端机型用户会真实反馈"启动慢""用久了卡"的团队。第二类是前端转移动开发的工程师,理解引擎运行机制能帮你少走很多原生层面的弯路。第三类是负责性能优化和发版的工程效能角色,灰度、回归、崩溃治理都和引擎特性挂钩。第四类是纯想做技术储备的人,花两三天理解预编译字节码、并发 GC、CDP 调试这些概念,对后面理解其他运行时也有帮助。
如果你团队的项目体量很小,用户都跑在最新旗舰机上,那 Hermes 的优势你可能短时间内感受不到。但不建议你放弃这部分知识,因为你迟早会碰到一个需要优化内存或启动速度的需求。到那时候再来"补课",成本远比你提前入门高。
2. 入门必学的第 1 件事:搞懂字节码和启动优化
我先说的第一件事就是字节码。很多人把 Hermes 等同于"换一个默认引擎",觉得 RN 0.70 之后新项目默认就是它,所以自己什么都不用学。这是最大的误区。你至少要知道代码从源码到可执行字节码的过程中,经历过什么,哪些操作会让优化失效,以及如何验证优化确实生效了。
2.1 预编译字节码为什么能让 App 启动更快
在典型的跨端项目中,开发者写出的 JS 文件最终会被打包成一个 bundle。传统引擎在处理这个 bundle 时,需要逐行读入源码、做词法分析、生成 AST 抽象语法树、再转为字节码或机器码,最后才执行。这套流程在每次冷启动时都会重复,即便你是同一个 bundle,没有任何变化。而 Hermes 的构建工具hermesc会在开发者构建 App 的时候,就把 bundle 编译成 HBC 字节码格式。App 安装包里的 JS 源码可以被字节码替代,运行时只需把这段字节码加载进内存,交给解释器执行即可。
省掉"源码分析和中间表示生成"这两个步骤,对启动阶段的影响非常明显。尤其是业务代码很多、首屏依赖模块很深的时候,解析源码的成本不是线性增长,而是层层展开的模块依赖,每多一层都要浪费一段 CPU 时间。HBC 的格式本身也更紧凑,一次磁盘读取就能把更多代码拉进内存,减少了 IO 等待。
值得注意的一点是,最理想的状态是发布包里直接跑 HBC。但在日常开发调试阶段,有些版本和工具链为了兼容 DevTools、热加载这些能力,可能仍然以 JS 源码方式执行。这大概率是你开启 Hermes 后,debug 模式没觉得启动变快的原因之一。真正的性能收益要在 release 构建里看,这点后面细说。
2.2 代码里哪些写法会抵消 Hermes 的优化
预编译字节码最大的敌人,就是运行时才被动态生成或执行的代码。最常见的两类是eval和new Function。这类代码没法在构建期被预编译,引擎只能临时退回解释执行,轻则多一次性能折损,重则让你整个 bundle 的一部分无法享受到 Hermes 优化。另一个非常隐蔽的问题是运行时拼接大段字符串再当作代码执行,这在很多老业务里其实是为了绕过模块系统而写出来的技巧。切 Hermes 之后这类代码一定要清理干净。
还有一种反向抵消:虽然引擎快了,但你业务侧一启动就同步执行大量模块的初始化代码,比如在顶层搞复杂计算、为几十个页面提前初始化路由状态、在入口模块里同步读取本地存储并做 JSON 解析。引擎只能保证它处理字节码这部分更高效,并不能消除你代码本身的同步阻塞。你要把启动路径上的"重活"延后或异步化。
建议你在构建时顺便做一下 bundle 分析,看看首屏启动时实际加载执行了哪些模块。某些模块可能根本没有被马上用到,只是被一个很早执行的入口文件依赖链拽了进来。把它按需拆出去,比单纯调引擎参数带来的提升更直接。
2.3 怎么看启动优化到底有没有生效
"变快了"不能靠感觉,要有数据。我建议你固定一台中低端 Android 真机,连着充电器、关掉后台其他进程,用 release 包做冷启动采样。每次启动前先杀掉进程,连续测 10 次取中位数,不要用平均值,平均值容易被偶尔一次极端值带偏。
如果你在 Android 上想快速看启动耗时,可以用系统自带的能力测 Activity 启动时间:
adb logcat -c adb shell am start -W -n com.example.app/.MainActivity执行完你会看到ThisTime、TotalTime、WaitTime三个时间字段,它们都能反映从系统启动页面到首帧的部分耗时。但要注意,am start -W的数据包含系统侧的时间调度,不完全等于业务首帧。更合理的做法是在业务代码里埋一个启动时间点,比如记录从引擎初始化到首屏可以交互的完整时间,再配合原生侧记录的进程启动时间,两边交叉验证。
启动优化有没有生效,核心就看这个"从进程创建到首帧可交互"的时间差。如果值降了 10% 以上,并且用户反馈的"打开白屏""点击没反应"明显减少,说明字节码的收益吃到了。如果数据没变化,大概率是你业务启动路径里有别的瓶颈,需要回到 2.2 里排查代码层面的问题,而不是继续调引擎。
3. 入门必学的第 2 件事:内存管理排查
内存是 Hermes 相对做得最激进、也最容易被用出问题的一环。很多人以为上了 Hermes 就自带内存豁免权,实际上内存压力确实降了,但泄漏还是会发生,只是发生在不同的层级。
3.1 内存占用为什么能降下来
Hermes 在内存上的优化思路可以拆成三点。
第一点是字节码本身不生成庞大的 AST 和中间表示,解析阶段产生的临时对象大量减少。你写的源码经过hermesc编译后,运行时加载的字节码是比源码更紧凑的二进制格式,解释器在启动时不需要额外保存结构复杂的语法树。
第二点是 GC 行为面向移动端做了调优。在通用 JS 引擎里,垃圾回收经常会在主线程上"Stop The World",也就是所有 JS 代码暂停一会儿,等着回收完成。这个停顿如果发生得频繁且时间长,用户就会感觉掉帧。Hermes 更多地把 GC 工作放到后台线程去执行,减少主线程停顿,降低因内存回收导致的卡顿次数。
第三点是它的内存分配策略相对保守,会尽量避免无意义的扩容和malloc碎片化。这对低端机尤其重要,因为系统在内存吃紧时会倾向于杀死那些占用增长快的进程。Hermes 让 App 的"内存增长曲线"更平缓,后台存活性自然提高,用户切回来不用重载页面。
不过要泼一盆冷水:引擎层面的优化解决不了你自己的对象泄漏。GC 只会回收"没有被引用"的对象,只要某个模块级变量一直保持着引用关系,该泄漏还是会泄漏,Hermes 只是让你泄漏的"速度"变慢而已。
3.2 常见的内存泄漏写法和排查思路
我在项目里排查过的最典型问题之一,是定时器没有清理。某页面在进入时启动了一个setInterval轮询接口,退出页面时没有调用clearInterval。在后续操作中,定时器的回调函数会一直存在,同时通过闭包间接持有页面里的状态对象和 DOM 或原生实例引用,导致页面永远无法被回收。切开 Hermes 后,这个问题的症状会变成内存曲线缓慢爬升,不会立刻崩溃,但时间一长后台被杀的概率会上升。
排查思路其实不复杂,关键在于分步。
第一步是复现问题的压力脚本。反复进入退出目标页面,操作特定路径,记录进入前和退出后当前页面的对象数量变化。如果退出后对象的数量基本没有回落,说明页面级的引用还挂在全局作用域里。第二步是触发一次内存快照,优先搜寻那些"退出页面后仍然存活"的对象。第三步就是沿着引用链往上追,看谁还在引用它。
这里有一个很多新手会忽略的点:GC 不是实时执行的,所以你在退出页面后立刻做快照,垃圾还没被回收,得到的数据可能很干净,也可能很脏。正确做法是退出页面后等一小段时间,或者先主动触发一次 GC,再做基线对比。否则你会把一些"待回收"但尚未回收的普通对象误判成泄漏。
3.3 内存快照分析与对象引用链解读
用 Hermes 做内存快照的能力,通常可以通过 CDP 调试协议暴露到开发工具里。打开调试器后,你大概率能找到一份 Heap Snapshot 或 Memory 面板。快照里的每个对象会显示三个关键数值:Shallow Size 表示对象自身占用的内存,Retained Size 表示对象以及它内部引用到的所有对象、且一旦它被回收就能释放的总内存,Distance 表示对象离 GC Root 有多远。
我推荐一个简单的排查顺序:先在快照里按 Retained Size 降序排列,找最大的一批对象,然后看 Distance 和引用链。如果一个页面的业务对象在快照里距离 GC Root 很近、还带着一长串 Retained Size,基本可以断定有模块级引用把它焊死了。曾经我定位到一个怪问题,列表反复滑动后内存只增不减,快照里看到一大片业务条目对象被保存在一个模块级 Map 里。顺着 Retained Size 链往上看,发现是一次请求成功回调没有移除缓存记录。这类问题不打开快照,只看代码是很难直接摸到的。
需要提醒的是,不同的工具和版本对快照导出的支持程度不一样,有些场景只能拿到调试模式下的快照,release 模式下没法直接给你导出分析。所以对于已经上线的版本,你用快照定位泄漏会受限,但配合崩溃日志和原生侧的内存监控,仍然可以把可疑模块锁定在一个比较小的范围内。
4. 入门必学的第 3 件事:调试与问题定位
提到引擎切换,很多人只关心性能和体积,忽略了一个实际上折腾时间最多的环节:调试。Hermes 的调试链路和传统 JS 调试有差异,如果还按浏览器里那套习惯来,第一个下午就会耗在"连不上调试器"上。
4.1 Hermes 的调试协议与传统 JS 调试的差异
Hermes 背后走的是基于 CDP 的调试通道,也就是说它完整支持断点、单步、作用域查看、控制台这类标准能力,这一点体验上和浏览器调试很接近。但关键差异在于,调试协议的实时对接是通过一个独立的 Inspector 进程完成的,你在通道里看到的对象、堆栈、CPU 和内存监控,全部来自 Hermes 运行时,而不是浏览器环境。
正因为有这个独立通道,连接链路变长了。在跨端开发流程里,你通常要先启动开发服务器,然后让 App 连上同一个调试端口,再在浏览器侧打开调试页面。任何一个环节断了,表现都是"调试器没有任何反应"。这也是新手最容易误判成"Hermes 不支持调试"的原因。
还有一个容易被忽略的点:当你开启调试器以后,Hermes 的运行模式可能会发生变化,某些 JIT 优化会被主动关闭,以保证断点和单步调试行为可预期。你在调试状态下看到的性能数据,不太代表 release 环境下的真实表现。该信调试器的时候信调试器,该用 release 包跑性能数据的时候,就老老实实做性能测试,不要混着看。
4.2 连接调试器、断点与堆快照的操作要点
连接链路我整理成一套标准动作,按顺序执行可以解决绝大多数连不上的问题。
第一步,保证开发服务器真的在跑,浏览器能打开对应的调试入口页面。第二步,在真机上打开开发者菜单,选择用 Hermes 调试的选项。第三步,确认手机和电脑在同一局域网内,或者用反向代理打通端口访问链路。Android 场景下最简单的一招是:
adb reverse tcp:8081 tcp:8081这条命令把手机的8081端口反向映射到电脑本地,手机上的调试请求就能通过这个通道回到开发服务器,很多"连不上"的怪问题都是靠它救回来的。执行完之后再去打开调试页面,基本一次能通。
连接成功后,我建议第一件事不是打断点,而是先在控制台输出HermesInternal这个全局对象,确认当前运行时真的是 Hermes。有时候你调试了半天,发现连的还是另一个执行环境,所有结论都建立在错误前提上。
断点调试本身没什么特殊技巧,嵌入源码映射(Source Map)后你就可以直接在业务源码文件里加断点。注意事项反而是"不要同时开多个调试客户端"。我遇到过反复点击调试按钮,浏览器开了十几个标签页,结果没有一个标签页真正拿到调试通道,最后全部关掉只保留一个,立刻恢复正常。另外,在真机上远程调试时,卡顿会比本地执行更明显,这是网络传输调试协议带来的正常开销,不是业务代码卡死。
4.3 Release 模式下的日志和崩溃定位
到这里必须强调一个很多人吃了大亏的细节:你在开发阶段习惯用的console.log,在 release 包里不一定能直接打到系统日志里。Hermes 把日志能力关联到了原生端,需要原生侧有对应的日志桥接,release 模式下如果不做配置,你就什么都看不到。
所以切换 Hermes 之前,先做一次原生日志透传的检查。确保你发布包里仍然能看到关键业务路径的日志,否则上线后某个功能异常时,你只能拿一个哑巴包去猜问题。
再来看崩溃定位。release 模式下运行的是 HBC 字节码,iOS 或 Android 的崩溃堆栈里底层能看到的可能是某条未知指令或某个引擎内部地址,而不是一行清晰的 JS 代码堆栈。这时候 Source Map 就是救命稻草。构建阶段生成 HBC 时,一定要把对应的 Source Map 保留归档,按版本号存好。线上出现崩溃,再把崩溃堆栈配合 Source Map 工具还原成 JS 源码级别的位置。很多团队上线时不注意保留 Source Map,等 App 崩了才发现无从下手,只能靠"复现+试错"来排查。
我有一个底线原则:任何构建产物都要附带版本号和 Source Map 归档。哪怕只是临时给测试发个灰度包,也要留一份能对上号的映射文件。这能让你在排查 release 问题时少熬一个通宵。
5. 入门必学的第 4 件事:工程配置与灰度验证
最后一件事,也是很多教程最不重视的一件事:工程上如何安全地把 Hermes 用起来。切换引擎不是改一行布尔值那么简单,它会影响构建流程、第三方依赖、崩溃治理和发布策略。如果少了灰度验证这一环,你几乎必定在某次上线后收到一堆用户反馈。
5.1 在 Android、iOS 上启用 Hermes 的具体配置
先说 Android 侧。较新的跨端项目里,你可以在gradle.properties中发现这样一个开关:
hermesEnabled=true把它设为true后重新构建,Gradle 构建链路就会自动把 JS bundle 编译成 HBC 字节码,并打对应版本的动态库进安装包。需要注意的是,如果你用的是较老的版本,可能还要在 App 模块的build.gradle里显式声明依赖匹配的引擎包,这部分建议直接以官方脚手架生成的结果为准。
iOS 侧对应的开关在 Podfile 里,通常是一行类似于下面的配置:
hermes_enabled = true修改之后执行pod install,重新把原生依赖装一遍。iOS 侧的一个常见坑是 Pod 缓存,你在 Podfile 里改了开关,但老缓存没清干净,最后打出来的包还是旧引擎。所以切完配置后建议顺手把Pods目录清掉重装一次,成本不高,但能避免很多"为什么改了没生效"的疑问。
怎么确认 App 里跑的到底是不是 Hermes?最简单的方法是在代码里判断:
if (global.HermesInternal) { // 当前运行时是 Hermes }在调试面板执行这行代码,如果返回真,说明引擎已经切换成功。你还可以检查安装包大小,通常开启 Hermes 后包体会增加若干 MB,因为里面多了一个引擎的动态库和 HBC 字节码,这也是上线前需要评估的指标之一。
5.2 分阶段灰度与回滚方案
我强烈不建议"代码合并后直接发全量"。引擎切换对运行时的行为有全局影响,第三方依赖的兼容性你不会事前全部发现,必须先灰度。
第一步,选一个独立的发布通道,先只让内部测试团队使用 Hermes 构建包。第二步,开放 10% 的用户流量,观察 48 小时,重点盯三套数据:启动耗时、崩溃率、内存阈值告警。第三步,如果数据平稳,扩到 50%,再观察一到两天。第四步,全量。
灰度的前提是准备一套可回滚的构建链路。不要为了省事把旧引擎的 release 构建配置直接删掉,你要能在一个小时内打回旧包并重新发布。回滚的触发条件也提前定好:崩溃率超出基线一定比例,或者某条核心路径的报错急剧上升,不需要等用户大规模投诉,先回滚再说。
灰度期间最容易暴露出来的问题就是第三方 SDK 的兼容性。有些 SDK 在初始化时会检测运行环境,如果全局对象不符合预期就会悄悄走降级或者直接抛异常。这些异常在测试阶段因为覆盖面小可能发现不了,放到 10% 流量上就很容易冒出苗头。你自己的集成代码要留好一个"开关切换"入口,保证出现问题能快速判定问题来源,而不是把所有责任都推到引擎上。
5.3 集成后的性能回归测试怎么做
灰度通过只代表"没有更差",不代表"变好了"。"变好了多少"要靠性能回归测试来说话。我这里给出一套可以照着做的指标组合。
| 指标 | 统计方式 | 建议判断口径 |
|---|---|---|
| 冷启动耗时 | 连续 10 轮冷启动取中位数 | 相比旧引擎下降 10% 以上为优 |
| TTI(可交互时间) | 记录首帧到业务事件可响应的时间 | 下降明显且没有反向波动 |
| 内存峰值 | 运行 10 分钟核心路径,抓取内存最高点 | 峰值不高于旧引擎 |
| 卡顿掉帧 | 滑动长列表时统计 16ms 帧超时次数 | 掉帧次数不上升 |
跑测的时候尽量使用 release 包,因为 debug 模式跑出来的数据和线上差距很大。设备矩阵至少要覆盖一台中低端 Android、一台主流 iPhone、一台最新旗舰机。低端机是最容易暴露引擎差异的试金石,只看旗舰机你会得出"毫无变化"的错误结论。
性能回归需要写在发布流程里,不是临时起意。我用过的做法是每周固定跑一次核心路径的压测,如果这周的冷启动耗时比起上周的基线异常上涨,就触发调查。这个机制会逼着你把每次构建的基线数据存下来,时间长了就是一个高质量的性能资产。
6. 踩坑实录:三个月里我最想删掉的 5 个操作
最后这部分,是三个月里真实踩过的坑的记录。我尽量按"问题现象 -> 原因 -> 解决思路"来整理,方便你对照排查。
6.1 依赖不兼容导致的启动白屏
现象是切换 Hermes 之后,某些页面在冷启动时直接白屏,控制台没有任何明显错误。后来定位发现是一个第三方依赖在初始化阶段使用了动态执行代码的方式来做配置加载,这类代码无法被预编译成 HBC,运行时又在早期初始化阶段才暴露问题。
解决思路是"先隔离,再替换"。把可疑依赖从启动链路里暂时摘除,确认白屏是否消失。如果确实是依赖问题,优先找替代库,或者把它下降到业务子模块里用异步延迟初始化处理。绝对不要为了迁就第三方依赖,而把 Hermes 关键特性关掉,那是捡了芝麻丢西瓜。
6.2 调试器连不上,卡了一下午
有一次在真机上怎么点调试选项都没反应,HBC 模式下调试面板一片空白。排查了一圈,发现是手机和电脑不在同一网络段,开发服务器的地址在手机端无法直接访问。最后用adb reverse tcp:8081 tcp:8081把端口打通,问题就解决了。
我后来形成的习惯是:每换一台测试机,第一件事就检查反向代理是否还活着,而不是一上来就怀疑 Hermes 配置有问题。调试链路涉及网络、端口、开发服务器、浏览器客户端四层,新手往往会忽略最底层的网络连通性。
6.3 混淆配置导致低端机偶发崩溃
某次 release 包内测时,低端机偶发崩溃,但高端机完全复现不出来。崩溃堆栈指向引擎内部,和业务代码看不出直接关系。最后顺着崩溃栈查到原生侧某个功能的类名和方法名被混淆规则改掉了,而那个方法又通过反射方式被调用。换了 Hermes 后,初始化顺序变了,这个反射调用路径被提前触发,问题才暴露出来。
解决方法是把需要反射调用的类在混淆规则里显式保留,同时把 Hermes 相关的包也一并加入白名单。配置改完后再做一轮内测,崩溃消失。
6.4 热更新与字节码兼容问题
我们在灰度阶段遇到的另一个坑,是服务端下发的热更新包,在部分用户手里加载失败。排查后发现,这些问题都集中在几个旧版本客户端,它们内置的 Hermes 版本较老,无法解析新产物里的字节码格式。引擎升级了,但热更新内容还按旧客户端兼容性在生成,两边没对齐。
解决思路是给热更新下发加一层版本协商:客户端先把引擎版本上报给服务端,服务端再决定下发哪个格式的更新包。字节码一旦升级,旧客户端就不能再收到新格式的补丁。这个问题如果不提前设计,上线后你会面临一份更新包只能覆盖一部分用户的窘境。
6.5 没做内存回归,release 暴雷
最让我印象深刻的,是一次没有任何预警的线上内存飙升。测试阶段大家把重点放在功能正确性上,没专门做长时间驻留页面的内存压测。上架后用户停留时间一长,系统内存占用节节攀升,后台进程频繁被杀,回到 App 就重建页面,体验非常差。
后来我们补上了压力测试脚本:持续停留、反复进出、模拟前后台切换,配合周期性内存快照定位到一个模块级缓存数组,它在每次查询后都追加结果但从不清理。修复后内存曲线恢复平稳。这件事之后,我再也不敢跳过内存回归这一环,它和功能测试同等重要。
三个月用下来,我最大的感受是:Hermes 本质上不是一次简单的性能升级,它迫使你从更接近运行时底层去思考代码。入门阶段别贪多,先把字节码、内存、调试、工程灰度这四件事理解透,其他的花哨技巧都是锦上添花。等这四块地基打牢,你再回去处理具体的 RN 版本差异或原生性能问题,会发现一切都顺很多。