跳至主要内容

捆绑版 Hermes

此页面概述了 **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 版本一起构建和测试的 JS 引擎,您可以使用它。

我们为什么要转向“捆绑版 Hermes”

从历史上看,React Native 和 Hermes 遵循两个 **不同的发布流程** 以及不同的版本控制。拥有不同的版本和不同的编号在 OSS 生态系统中造成了混乱,因为不清楚 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,并确保在构建 Hermes 时仅使用一个 JSI 副本。

因此,我们可以在发布 React Native 版本时发布 Hermes 版本,并确保我们构建的 Hermes 引擎与我们正在发布的 React Native 版本 **完全兼容**。我们正在与我们正在进行的 React Native 版本一起发布此版本的 Hermes,因此得名 *捆绑版 Hermes*。

这将如何影响应用开发者

如引言中所述,如果您是应用开发者,此更改 **不应直接影响** 您。

以下段落描述了我们在幕后进行的更改,并解释了一些理由,以确保透明度。

iOS 用户

在 iOS 上,我们已移动了您正在使用的 hermes-engine

在 React Native 0.69 之前,用户将下载一个 pod(您可以在此处找到 podspec)。

在 React Native 0.69 上,用户将改为使用在 react-native NPM 包中的 sdks/hermes-engine/hermes-engine.podspec 文件中定义的 podspec。该 podspec 依赖于我们作为 React Native 发布流程的一部分上传到 Maven 和 React Native GitHub 发布的 Hermes 预构建 tarball(例如,查看此版本的资产)。

Android 用户

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

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/react-native 存储库内的 facebook/hermes 存储库下载一个包含 Hermes 源代码的 tarball**。我们对其他原生依赖项(Folly、Glog 等)也采用了类似的机制,并且我们使 Hermes 遵循相同的设置。

main 构建 React Native 时,我们将获取 facebook/hermesmain 的 tarball,并在 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 实现细节

为了在 Android 上实现这一点,我们在 React Native 的 /ReactAndroid/hermes-engine 中添加了一个新的构建,它将负责构建 Hermes 并打包以供使用(在此处查看更多上下文)。

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

// 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 使用非预制方法(即解压缩 .aar 并提取 .so 文件)来使用 fbjni。Hermes 引擎和其他库则使用预制方法来使用 fbjni。我们正在研究 未来解决此问题,以便 Hermes 导入将是一行代码。

iOS 实现细节

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

  • /scripts/hermes。这些脚本包含下载 Hermes tarball、解压缩它以及配置 iOS 构建的逻辑。如果您将 hermes_enabled 字段设置为 true,则在 pod install 时会调用它们。
  • /sdks/hermes-engine。这些脚本包含实际上构建 Hermes 的构建逻辑。它们是从 facebook/hermes 存储库复制并改编而来,以便在 React Native 中正确工作。具体来说,utils 文件夹内的脚本负责为所有 Mac 平台构建 Hermes。

Hermes 是作为 CircleCI 上的 build_hermes_macos 作业的一部分构建的。该作业将生成一个 tarball 作为工件,当使用已发布的 React Native 版本时,hermes-engine podspec 将下载该工件(这是在 build_hermes_macos 中为 React Native 0.69 创建的工件示例)。

预构建的 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 版本,则可以通过在使用 CocoaPods 时将 CI 环境变量设置为 true 来强制从源代码构建 Hermes:CI=true pod install

调试符号

Hermes 的预构建工件默认不包含调试符号 (dSYM)。我们计划将来为每个版本分发这些调试符号。在此之前,如果您需要 Hermes 的调试符号,则需要从源代码构建 Hermes。一个 hermes.framework.dSYM 将在构建目录中与每个 Hermes 框架一起创建。

我担心此更改会影响我

我们想强调的是,这本质上是在哪里构建 Hermes 和如何在两个存储库之间同步代码的组织更改。此更改应该对我们的用户完全透明。

从历史上看,我们过去会为特定版本的 React Native 发布 Hermes 版本(例如,针对 RN0.68.x 的 v0.11.0)。

使用“捆绑的 Hermes”,您可以改为依赖一个标签,该标签将表示在发布特定版本的 React Native 时使用的版本。