捆绑 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 版本完全兼容。我们将此版本的 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 依赖于我们上传到 Maven 和 React Native GitHub Release 的 Hermes 预构建 tarball,作为 React Native 发布过程的一部分(即 请参阅此版本的资产)。
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 App 的用户需要执行以下额外步骤才能使构建正常工作
- 确保环境配置正确,包括 Android SDK 和 node。
- 使用 Chocolatey 安装 cmake
- 安装以下任一项
- Visual Studio 2022 的构建工具.
- Visual Studio 22 Community Edition - 仅选择 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 在底层是如何工作的?
此机制依赖于从 facebook/hermes
存储库中的 facebook/react-native
存储库下载包含 Hermes 源代码的 tarball。我们为其他原生依赖项(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 引擎的构建
// 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 工件是变体感知的,并且会将引擎的 debug 构建与您的应用的 debug 构建正确匹配。您无需在此处进行任何自定义配置(即使您使用 staging
或其他构建类型/风味)。
但是,这使得模板中需要这一行
exclude group:'com.facebook.fbjni'
这是必需的,因为 React Native 正在使用非 prefab 方法(即,解压缩 .aar
并提取 .so
文件)来使用 fbjni
。Hermes-engine 和其他库正在使用 prefab 来代替使用 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 的预构建工件不包含调试符号 (dSYM)。我们计划在未来为每个版本分发这些调试符号。在此之前,如果您需要 Hermes 的调试符号,您将需要从源代码构建 Hermes。hermes.framework.dSYM
将在构建目录中与每个 Hermes 框架一起创建。
我担心此更改会影响我
我们想强调,这本质上是对 Hermes 的构建位置 和 代码在两个存储库之间同步方式 的组织变更。此更改对于我们的用户应该是完全透明的。
从历史上看,我们过去常常为特定版本的 React Native 发布 Hermes 版本(例如,v0.11.0 for RN0.68.x
)。
使用“捆绑 Hermes”,您可以改为依赖一个标签,该标签将表示在切割特定版本的 React Native 时使用的版本。