捆绑 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 依赖于 Hermes 的预构建 tarball,我们将其上传到 Maven 和 React Native GitHub Release,作为 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 应用的用户需要遵循以下额外步骤才能使构建正常工作
- 确保环境配置正确,包括 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 在底层是如何工作的?
此机制依赖于从 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 工件是**变体感知的**,并且会将引擎的调试构建与您的应用的调试构建正确匹配。您不需要在此处进行任何自定义配置(即使您使用 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 本身将使用 build-hermes-xcode.sh
脚本作为 Xcode 构建管道的一部分进行构建。
从源代码构建 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 时使用的版本。