捆绑式 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 版本完全兼容。我们正在随 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 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.aar
和 hermes-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 应用程序的用户需要遵循以下额外步骤,以确保构建正常工作
- 确保环境已正确配置,包括 Android SDK 和 Node。
- 使用 Chocolatey 安装 cmake
- 安装以下任一:
- Visual Studio 2022 的构建工具.
- Visual Studio 22 社区版 - 仅选择 C++ 桌面开发即可。
- 确保Visual Studio 命令提示符已正确配置。这是必需的,因为正确的 C++ 编译器环境变量是在这些命令提示符中配置的。
- 在 Visual Studio 命令提示符中运行应用程序,使用
npx react-native run-android
。
用户仍然可以使用其他引擎吗?
是的,用户可以自由启用/禁用 Hermes(Android 上使用 enableHermes
变量,iOS 上使用 hermes_enabled
)。“捆绑式 Hermes”的更改只会影响 Hermes 为您构建和捆绑的方式。
从 React Native 0.70 开始,enableHermes
/hermes_enabled
的默认值为 true
。
这将如何影响贡献者和扩展开发者
如果您是 React Native 贡献者,或者您正在 React Native 或 Hermes 之上构建扩展,请继续阅读,我们将解释捆绑式 Hermes 的工作原理。
捆绑式 Hermes 的内部工作原理是什么?
此机制依赖于将包含 Hermes 源代码的 tarball 从 facebook/hermes
仓库下载到 facebook/react-native
仓库内部。我们对其他原生依赖项(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 submodule。
如果您正在 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
分支。
您无需在机器上安装额外的工具(例如 cmake
、ninja
或 python3
),因为我们已将构建配置为使用这些工具的 NDK 版本。
在 Gradle 消费端,我们还进行了一项小的改进:我们从 releaseImplementation
和 debugImplementation
切换到了 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 将下载该 tarball(这里是为 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 版本,您可以通过在使用 CocoaPods 时将 CI
环境变量设置为 true
来强制从源代码构建 Hermes:CI=true pod install
。
调试符号
Hermes 的预构建制品默认不包含调试符号 (dSYMs)。我们计划未来在每个版本中分发这些调试符号。在此之前,如果您需要 Hermes 的调试符号,您将需要从源代码构建 Hermes。一个 hermes.framework.dSYM
将在构建目录中与每个 Hermes 框架一同创建。
我担心此更改会影响我
我们想强调的是,这本质上是关于 Hermes 在哪里构建以及代码在两个仓库之间如何同步的组织性更改。此更改对我们的用户应该是完全透明的。
从历史上看,我们过去会为特定版本的 React Native 发布 Hermes(例如 v0.11.0 适用于 RN0.68.x
)。
有了“捆绑式 Hermes”,您可以转而依赖一个标签,该标签将代表发布特定版本 React Native 时所使用的版本。