跳至主内容

捆绑式 Hermes

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

本文概述了 Hermes 和 React Native 的构建机制

如需在应用中使用 Hermes 的具体操作指南,请参阅另一篇文档:使用 Hermes

注意

请注意:本文属于技术深度解析,主要面向基于 Hermes 或 React Native 开发扩展组件的用户。普通 React Native 用户无需深入了解 React Native 与 Hermes 的交互细节。

什么是「捆绑式 Hermes」

从 React Native 0.69.0 开始,每个 React Native 版本都将配套构建对应的 Hermes 版本。我们将这种分发模式称为 捆绑式 Hermes

从 0.69 版本起,您始终可以使用与每个 React Native 版本配套构建并测试的 JavaScript 引擎。

为何采用「捆绑式 Hermes」

历史上,React Native 和 Hermes 采用两套独立的发布流程和版本号体系。这种独立发布模式导致开源生态出现混淆——开发者难以确定特定 Hermes 版本是否兼容特定 React Native 版本(例如需要明确知道 Hermes 0.11.0 仅兼容 React Native 0.68.0 等)。

Hermes 和 React Native 共享 JSI 代码(Hermes 代码在此React Native 代码在此)。若两份 JSI 代码不同步,构建的 Hermes 将无法兼容对应 React Native 版本。您可在此处详细了解该 ABI 兼容性问题

为解决此问题,我们扩展了 React Native 发布流程:在构建 Hermes 时下载并编译其源代码,同时确保仅使用单份 JSI 代码。

由此,我们能在发布每个 React Native 版本时同步发布配套的 Hermes 版本,并确保该 Hermes 引擎与当前 React Native 版本完全兼容。我们将此 Hermes 版本与 React Native 版本共同分发,故命名为「捆绑式 Hermes」。

对应用开发者的影响

如开篇所述,如果您是应用开发者,此变更不会直接影响您的开发工作。

以下段落将详细说明底层变更及其设计考量,以便保持技术透明度。

iOS 用户

在 iOS 平台,我们调整了您使用的 hermes-engine 依赖来源。

在 React Native 0.69 之前,用户需下载 CocoaPods 依赖(相关 podspec 文件在此)。

在 React Native 0.69 中,用户将改用 react-native NPM 包内 sdks/hermes-engine/hermes-engine.podspec 定义的 podspec。该 podspec 依赖预构建的 Hermes 压缩包,此压缩包会作为 React Native 发布流程的一部分上传至 Maven 和 GitHub Release(例如此版本的资源文件)。

Android 用户

在 Android 平台,我们将按以下方式更新默认模板中的 android/app/build.gradle 文件:

diff
dependencies {
// ...

if (enableHermes) {
+ implementation("com.facebook.react:hermes-engine:+") {
+ exclude group:'com.facebook.fbjni'
+ }
- def hermesPath = "../../node_modules/hermes-engine/android/";
- debugImplementation files(hermesPath + "hermes-debug.aar")
- releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}

在 React Native 0.69 之前,用户需要从 hermes-engine NPM 包中引入 hermes-debug.aarhermes-release.aar

在 React Native 0.69 中,用户将从 react-native NPM 包的 android/com/facebook/react/hermes-engine/ 目录中获取 Android 多构建变体产物。 请注意,我们将在未来的 React Native 版本中完全移除hermes-engine 的依赖。

使用新架构的 Android 用户

由于本地代码构建机制的特性(即 NDK 的使用方式),采用新架构的用户将从源代码构建 Hermes

这使得新架构用户的 React Native 和 Hermes 构建机制保持一致(两者都将从源码构建)。 这意味着此类 Android 用户在首次构建时可能会遇到构建性能下降的问题。

您可以在加速构建流程页面找到优化构建时间、减少影响的指导方案。

在 Windows 上使用新架构的 Android 用户

在 Windows 机器上使用新架构构建 React Native 应用的用户,需要遵循以下额外步骤确保构建正常工作:

用户是否仍可使用其他引擎?

可以,用户可自由启用/禁用 Hermes(通过 Android 的 enableHermes 参数或 iOS 的 hermes_enabled 参数)。 "捆绑式 Hermes"的变更仅影响 Hermes 的构建和打包方式

从 React Native 0.70 开始,enableHermes/hermes_enabled 的默认值将设为 true

对贡献者和扩展开发者的影响

如果您是 React Native 贡献者,或正在开发基于 React Native/Hermes 的扩展,请继续阅读了解捆绑式 Hermes 的工作机制。

捆绑式 Hermes 的底层原理是什么?

该机制依赖于从 facebook/hermes 仓库下载源代码压缩包facebook/react-native 仓库。此机制与其他原生依赖项(Folly、Glog 等)的处理方式一致,我们将 Hermes 也纳入了相同体系。

在从 main 构建 React Native 时,我们会获取 facebook/hermes 的 main 分支的压缩包,并将其作为 React Native 构建流程的一部分进行编译。

在从发布分支(例如 0.69-stable)构建 React Native 时,我们会使用 Hermes 仓库的特定标签同步两个仓库的代码。使用的标签名称将存储在 React Native 发布分支的 sdks/.hermesversion 文件中(例如 0.69 发布分支上的该文件)。

从某种意义上说,你可以将此方法类比为 git 子模块

如果你基于 Hermes 进行开发,可以通过这些标签确认构建 React Native 时使用的 Hermes 版本,因为标签名称中会包含 React Native 版本号(例如 hermes-2022-05-20-RNv0.69.0-ee8941b8874132b8f83e4486b63ed5c19fc3f111)。

Android 实现细节

为实现此功能,我们在 React Native 的 /ReactAndroid/hermes-engine 目录中新增了构建流程,负责编译 Hermes 并打包供使用(更多背景参见此处)。

现在可以通过调用以下命令触发 Hermes 引擎构建:

bash
// Build a debug version of Hermes
./gradlew :ReactAndroid:hermes-engine:assembleDebug
// Build a release version of Hermes
./gradlew :ReactAndroid:hermes-engine:assembleRelease

来自 React Native main 分支。

你无需在机器上安装额外工具(如 cmakeninjapython3),因为我们已配置构建流程使用 NDK 自带的工具版本。

在 Gradle 使用端,我们也进行了优化:从 releaseImplementationdebugImplementation 改为使用 implementation。这是因为新版 hermes-engine Android 组件支持变体感知,能自动匹配应用的调试版/发布版引擎。即使你使用 staging 或其他构建类型/风味也无需额外配置。

但此变更导致模板中需要保留这行配置:

exclude group:'com.facebook.fbjni'

这是因为 React Native 采用非 Prefab 方式使用 fbjni(即解压 .aar 提取 .so 文件),而 Hermes-engine 等库使用 Prefab 方式引用 fbjni。我们正在研究解决此问题,未来引入 Hermes 将只需单行配置。

iOS 实现细节

iOS 实现依赖于以下位置的系列脚本:

  • /scripts/hermes:这些脚本包含下载 Hermes 压缩包、解压及配置 iOS 构建的逻辑。当 hermes_enabled 设为 true 时,执行 pod install 将自动调用它们。

  • /sdks/hermes-engine:这些脚本包含实际的 Hermes 构建逻辑,它们从 facebook/hermes 仓库复制并适配,确保能在 React Native 环境中正常运行。其中 utils 目录的脚本专门负责为所有 Mac 平台构建 Hermes。

Hermes 在 CircleCI 的 build_hermes_macos 任务中构建,产出的压缩包将作为产物。发布 React Native 版本时,hermes-engine podspec 会下载该压缩包(此处是 React Native 0.69 在 build_hermes_macos 中生成的产物示例)。

预构建的 Hermes

如果当前使用的 React Native 版本没有预构建产物(例如,你可能在使用来自 main 分支的 React Native),那么 Hermes 就需要从源代码构建。首先,Hermes 编译器 hermesc 会在 pod install 过程中为 macOS 构建,然后 Hermes 本身将作为 Xcode 构建流程的一部分,使用 build-hermes-xcode.sh 脚本来构建。

从源代码构建 Hermes

当使用来自 main 分支的 React Native 时,Hermes 总是从源代码构建。如果你使用的是稳定的 React Native 版本,可以通过将环境变量 CI 设置为 true 在使用 CocoaPods 时强制从源代码构建:CI=true pod install

调试符号

默认情况下,Hermes 的预构建产物不包含调试符号(dSYMs)。我们计划在未来为每个版本分发这些调试符号。在此之前,如果你需要 Hermes 的调试符号,就需要从源代码构建。构建目录中会为每个 Hermes 框架生成对应的 hermes.framework.dSYM 文件。

我担心这个变更会影响到我

我们想强调,这本质上是对 Hermes 构建位置以及两个仓库间代码同步方式的组织结构调整。这一变更对用户而言应完全透明。

历史上,我们曾为特定版本的 React Native 发布 Hermes(例如 v0.11.0 for RN0.68.x)。

使用 'Bundled Hermes' 后,你可以依赖一个标签来确认发布特定 React Native 版本时所使用的 Hermes 版本。