Hermes — 是一个带有AOT(Ahead-of-Time)编译的JavaScript引擎,由Meta为React Native开发,并针对内存有限的移动设备进行了优化。根据Meta Engineering官方博客(2022),与JavaScriptCore相比,Hermes将应用程序启动时间减少了20-50%,并将捆绑包大小减少了30-40%。与V8或JSC不同,Hermes不在设备上使用JIT编译 — 所有JavaScript在构建阶段通过Hermes CLI编译成字节码。这对于iOS尤其重要,因为JIT编译受到App Store策略的限制。
要点
Hermes — 是一个紧凑的开源(MIT许可证)JavaScript引擎,由Meta为React Native创建,并针对在资源有限的移动设备上运行进行了优化。Hermes的主要创新是放弃JIT编译,转而采用AOT(Ahead-of-Time)。在React Native应用程序的构建阶段,Metro Bundler将编译后的JavaScript传递给Hermes CLI,后者将其转换为HBC(Hermes ByteCode)字节码。该字节码由引擎直接在设备上执行,无需额外编译。这种方法提供了可预测的性能:没有JIT「预热」,没有编译暂停,没有额外电池消耗。Hermes的设计考虑了移动设备的限制:RAM小(1-4 GB)、能耗有限以及需要快速冷启动。Hermes的首次公开发布是在2019年,从React Native 0.70(2022)开始,该引擎成为Android上的标准。
JavaScriptCore(JSC)— 标准的WebKit引擎,由Safari和0.70版本之前的React Native使用。JSC支持JIT编译,为复杂的JavaScript操作提供了高性能。但JIT需要预热:前几秒代码运行较慢(解释模式),然后JIT编译热点部分。在iOS上,由于App Store策略(禁止动态代码生成),JIT实际上不可用,因此JSC在iOS上仅以解释模式运行 — 性能下降。JSC具有更大的二进制大小(约10 MB),并且由于JIT基础设施而消耗更多RAM。Hermes不依赖JIT,可在启动后立即提供可预测的性能(冷启动)。JSC更完整地支持ECMAScript标准(包括Proxy、BigInt、Reflect),但代价是更高的资源消耗。对于冷启动和小尺寸至关重要的React Native项目 — Hermes更受青睐。对于具有重JS计算的项目(游戏、WebGL)— JSC可以提供更高的峰值性能。
| 参数 | Hermes | JavaScriptCore |
|---|---|---|
| 编译 | AOT(构建阶段) | JIT + 解释(设备上) |
| 冷启动 | 快20-50% | 基准水平 |
| 捆绑包大小 | 小30-40% | 基准水平 |
| RAM消耗 | 少20-30% | 基准水平 |
| ECMAScript | ES2020(有限制) | ES2022+(完整) |
| iOS JIT | 不需要 | 不可用(仅解释) |
| 二进制 | ~3 MB | ~10 MB |
AOT编译(Ahead-of-Time)在Hermes中分两个阶段进行。在第一阶段,Metro Bundler将JavaScript文件收集到单个捆绑包中,并将其传递给Hermes CLI(hermesc工具)。在第二阶段,hermesc解析JavaScript AST,生成中间表示HIR(Hermes Intermediate Representation),然后发出二进制HBC字节码。结果是一个扩展名为.hbc的文件,仅包含字节码而不包含原始JavaScript。Hermes运行时直接加载HBC,无需解析和编译。这从根本上加快了启动速度:引擎无需解析数千行JS(异步),而是读取预编译的二进制格式。AOT还减小了大小:字节码平均比JavaScript AST紧凑30%。AOT的缺点是无法在运行时执行eval、new Function或动态require — 所有模块必须在构建阶段已知。
# 单独安装Hermes CLI
npm install hermes-engine
# 将JS编译为HBC字节码
npx hermesc -emit-binary -out bundle.hbc bundle.js
# 字节码统计
npx hermesc -dump-bytecode bundle.hbc # shows HBC instructions
# 原始JS与HBC大小
wc -c bundle.js # 2,300,000 bytes
wc -c bundle.hbc # 1,450,000 bytes (37% reduction)
Hermes的性能通过三个关键指标来衡量:Time-To-Interactive(TTI)、APK/IPA大小和RAM消耗。据Meta称,在Android上,与JSC相比,Hermes将TTI降低了34%:在中端设备(Moto G7)上从4.2秒降至2.8秒。由于紧凑的字节码和没有JIT库,APK大小减少了28%。相同负载下RAM消耗平均降低22% — 这对于具有2-3 GB RAM的设备尤其重要。在iOS上,优势甚至更大:由于JSC无法使用JIT,Hermes将TTI提升了高达45%。这些指标基于Meta使用Facebook Lite应用程序进行的测试。在实际项目中,优势各不相同:对于简单屏幕(列表、文本),Hermes提供更大的改进;对于繁重的动画,改进较小。性能分析工具:React Native Profiler + hermes profile --heap。
Hermes包含一个内置内存分析器,可通过Chrome DevTools访问。通过Metro连接到应用程序,打开Memory选项卡,在快照类型中选择Hermes(JavaScript)。Hermes支持三种快照类型:Heap Snapshot(所有对象)、Allocation Timeline(对象生命周期)和Allocation Sampling(选择性分析)。Hades GC将GC暂停次数降至最低 — 平均每10秒工作一次2-5ms的暂停,而JSC在同一时期有10-20ms的暂停。
Hades GC — 是Hermes中的垃圾回收器,专为具有最小暂停的移动场景而设计。与JSC中的mark-sweep GC不同,Hades使用并发(concurrent)回收:回收器与主执行线程并行工作,仅将其暂停短暂时间。Hades GC将堆分为几代:年轻代(nursery)经常快速回收(Scavenge),老年代回收频率较低且暂停时间更短。堆大小可配置:默认为应用程序可用RAM的2/3,最小阈值为32 MB。Hades不使用stop-the-world方法:即使老年代的完整回收也不会超过5-8ms。回收器针对典型的移动场景进行了优化:许多短期对象(临时字符串、React fibre对象),少量长期对象。对于具有密集对象创建的应用程序(列表、动画),与JSC相比,Hades提供更流畅的FPS。
启用Hermes取决于React Native版本和平台。从React Native 0.70开始,Hermes在新项目中默认对Android启用。对于iOS,Hermes是可选的。在Android上启用:在android/app/build.gradle文件中,在project.ext.react.enableHermes块中设置enableHermes: true。对于iOS:通过use_react_native!(:path => config[:reactNativePath], :hermes_enabled => true)在Podfile中将hermes_enabled设置为true。启用后,执行pod install。对于现有项目,检查库的兼容性:Hermes不支持Proxy、eval和一些ES2021功能。使用npx react-native info检查配置。要在Hermes和JSC之间切换,更改标志并执行clean rebuild。
// android/app/build.gradle — 启用Hermes
project.ext.react = [
enableHermes: true,
cliPath: "node_modules/react-native/cli.js"
]
// iOS/Podfile — 在iOS上启用Hermes
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'MyApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => true
)
end
Hermes的主要限制与放弃JIT有关。Hermes不支持:Proxy和Reflect API(用于MobX、Vue、某些状态管理器),BigInt(大数字),Symbol.toStringTag,WeakRef和FinalizationRegistry。eval和new Function函数在执行时生成异常。Array.prototype.flat和flatMap方法使用this参数的回调在限制下工作。带时区的ISO 8601日期格式未完全处理。这些限制中的大多数不会影响典型的React Native应用程序:React和React Native使用有限的功能集。如果库需要Proxy(例如,使用Proxy的MobX 6+),使用configure({ useProxies: false })或选择替代方案。要检查现有代码的兼容性,在您的捆绑包上运行npx hermesc -check — 它将显示不支持的功能列表。
常见问题
在应用程序控制台中执行console.log(global.HermesInternal)。如果HermesInternal对象存在 — 应用程序在Hermes上运行。替代方法:console.log(global.HermesInternal?.getRuntimeProperties()) — 将显示引擎版本和GC参数。在Release版本中,HermesInternal可能不可用以减小大小。
检查Podfile设置:iOS上的Hermes需要New Architecture(Fabric Renderer)。设置:hermes_enabled => true,执行pod install --repo-update。如果项目从React Native 0.70以下版本升级,检查库与New Architecture的兼容性。如果第三方库需要JSC,请禁用Hermes — 为此将标志更改为false并重新安装Pods。
不会,Hermes不会影响热重载/快速刷新。在开发期间,Metro运行JavaScript捆绑包时不进行Hermes编译(纯JS)。Hermes字节码仅为Release版本创建。在Debug模式下,使用标准的JavaScriptCore或Hermes的解释模式。当Hermes在配置中启用时,热重载速度不会改变 — 切换仅在生产构建阶段发生。
可以,从Expo SDK 45开始,Hermes支持managed workflow。在app.json中设置"jsEngine": "hermes"。对于bare workflow,Hermes的工作方式与普通React Native项目相同。Expo Go不支持Hermes — 使用Expo Dev Client或EAS Build进行带Hermes的构建。通过expo doctor检查库的兼容性。
使用React Native Performance Monitor(FPS指标)和Hermes Profiling Tools。在同一设备上构建两个版本 — 带Hermes和JSC。测量:冷启动(从点击图标到第一个交互屏幕)、TTI(Time-To-Interactive)、APK/IPA大小和峰值RAM消耗。对每个配置至少运行3次测试。Hermes的典型优势:启动速度快20-40%,RAM少15-25%。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。