跳到主要内容

捆绑 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 遵循两个不同的发布流程,具有不同的版本控制。拥有不同版本号的不同版本在开源生态系统中造成了混淆,不清楚特定版本的 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 版本完全兼容。我们将此版本的 Hermes 与我们发布的 React Native 版本一起发布,因此得名捆绑 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 Release 的预构建 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 多变体 artifact。另请注意,我们将在 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 存储库下载一个 tarball,其中包含 Hermes 源代码。我们对其他本机依赖项(Folly、Glog 等)也采用了类似的机制,我们调整了 Hermes 以遵循相同的设置。

main 构建 React Native 时,我们将获取 facebook/hermes 的 main 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 引擎的构建

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 artifact 是变体感知的,并且会将引擎的调试构建与您的应用程序的调试构建正确匹配。您不需要任何自定义配置(即使您使用 staging 或其他构建类型/变体)。

然而,这使得模板中的这一行成为必需

exclude group:'com.facebook.fbjni'

这是必需的,因为 React Native 使用非预制方法(即解压 .aar 并提取 .so 文件)来使用 fbjni。Hermes-engine 和其他库则使用预制来使用 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 作为 artifact,当使用已发布的 React Native 版本时,hermes-engine podspec 将下载该 tarball(此处是为 React Native 0.69 在 build_hermes_macos 中创建的 artifact 示例)。

预构建 Hermes

如果正在使用的 React Native 版本没有预构建的 artifact(即,您可能正在使用 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 pod installCI 环境变量设置为 true 来强制从源代码构建 Hermes。

调试符号

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

我担心这个改变会影响我

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

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

通过“捆绑 Hermes”,您可以使用一个标签来表示在发布特定版本的 React Native 时所使用的版本。