加快构建阶段构建你的 React Native 应用可能非常耗时,需要花费开发人员几分钟的时间。随着项目的发展,以及在拥有多个 React Native 开发人员的大型组织中,这可能会成为一个问题。
为了缓解这种性能瓶颈,本文档分享了一些关于如何提高构建时间的建议。
信息请注意,这些建议是一些高级功能,需要对原生构建工具的工作原理有一定的了解。
开发期间仅构建一个 ABI (仅限 Android)
在本地构建 Android 应用时,默认情况下你会构建所有 4 个 应用程序二进制接口 (ABI):armeabi-v7a、arm64-v8a、x86 和 x86_64。
但是,如果你只是在本地构建并在模拟器或物理设备上进行测试,可能不需要构建所有这些 ABI。
这应该可以将你的原生构建时间减少约 75%。
如果你使用的是 React Native CLI,可以在 run-android 命令中添加 --active-arch-only 标志。此标志将确保从正在运行的模拟器或连接的手机中选择正确的 ABI。要确认此方法是否正常工作,你会在控制台中看到类似 info Detected architectures arm64-v8a 的消息。
$ yarn react-native run-android --active-arch-only[ ... ]info Running jetifier to migrate libraries to AndroidX. You can disable it using "--no-jetifier" flag.Jetifier found 1037 file(s) to forward-jetify. Using 32 workers...info JS server already running.info Detected architectures arm64-v8ainfo Installing the app...
此机制依赖于 reactNativeArchitectures Gradle 属性。
因此,如果你直接通过命令行 Gradle 进行构建而不使用 CLI,可以按如下方式指定要构建的 ABI:
$ ./gradlew :app:assembleDebug -PreactNativeArchitectures=x86,x86_64
这在你希望在 CI 上构建 Android 应用并使用矩阵来并行构建不同架构时可能很有用。
如果需要,你也可以在本地覆盖此值,方法是在项目的顶层文件夹中使用 gradle.properties 文件。
# Use this property to specify which architecture you want to build.# You can also override it from the CLI using# ./gradlew
构建应用的发布版本后,请不要忘记删除这些标志,因为你希望构建一个适用于所有 ABI 的 apk/app bundle,而不仅仅是你日常开发工作流中使用的那个。
启用配置缓存 (仅限 Android)
从 React Native 0.79 开始,你还可以启用 Gradle 配置缓存。
当你使用 yarn android 运行 Android 构建时,你将执行一个由两个步骤组成的 Gradle 构建(来源):
配置阶段,在此阶段会评估所有 .gradle 文件。
执行阶段,在此阶段会实际执行任务,从而编译 Java/Kotlin 代码等。
你现在可以启用配置缓存,这将允许你在后续构建中跳过配置阶段。
这在频繁更改原生代码时很有益,因为它能提高构建速度。
例如,在这里你可以看到在原生代码发生更改后,重新构建 RN-Tester 的速度有多快。
通过在 android/gradle.properties 文件中添加以下行来启用 Gradle 配置缓存:
org.gradle.configuration-cache=true
有关配置缓存的更多资源,请参阅官方 Gradle 文档。
使用 Maven 镜像 (仅限 Android)
构建 Android 应用时,你的 Gradle 构建需要从 Maven Central 和其他存储库从互联网下载必要的依赖项。
如果你的组织运行着 Maven 存储库镜像,你应该考虑使用它,因为它将通过从镜像下载工件而不是从互联网下载来加速你的构建。
你可以通过在 android/gradle.properties 文件中指定 exclusiveEnterpriseRepository 属性来配置镜像:
差异# Use this property to enable or disable the Hermes JS engine.# If set to false, you will be using JSC instead.hermesEnabled=true# Use this property to configure a Maven enterprise repository# that will be used exclusively to fetch all of your dependencies.+exclusiveEnterpriseRepository=https://my.internal.proxy.net/
通过设置此属性,你的构建将仅从你指定的存储库获取依赖项,而不会从其他存储库获取。
使用编译器缓存
如果你频繁进行原生构建(C++ 或 Objective-C),你可能会从使用编译器缓存中受益。
具体来说,你可以使用两种类型的缓存:本地编译器缓存和分布式编译器缓存。
本地缓存
信息以下说明适用于Android 和 iOS。如果你只构建 Android 应用,则一切顺利。如果你也构建 iOS 应用,请按照下面的 Xcode 特定设置 部分中的说明进行操作。
我们建议使用 ccache 来缓存原生构建的编译。Ccache 通过包装 C++ 编译器,存储编译结果,并在之前存储了中间编译结果时跳过编译来工作。
Ccache 在大多数操作系统的包管理器中都可用。在 macOS 上,我们可以使用 brew install ccache 安装 ccache。或者,你可以按照 官方安装说明 从源代码安装。
然后,你可以进行两次干净构建(例如,在 Android 上,你可以先运行 yarn react-native run-android,删除 android/app/build 文件夹,然后再次运行第一个命令)。你会注意到第二次构建比第一次快得多(应该只需要几秒钟而不是几分钟)。构建时,你可以验证 ccache 是否正常工作,并通过 ccache -s 检查缓存命中/未命中率。
$ ccache -sSummary: Hits: 196 / 3068 (6.39 %) Direct: 0 / 3068 (0.00 %) Preprocessed: 196 / 3068 (6.39 %) Misses: 2872 Direct: 3068 Preprocessed: 2872 Uncacheable: 1Primary storage: Hits: 196 / 6136 (3.19 %) Misses: 5940 Cache size (GB): 0.60 / 20.00 (3.00 %)
请注意,ccache 会聚合所有构建的统计信息。你可以使用 ccache --zero-stats 在构建前重置它们,以验证缓存命中率。
如果你需要清除缓存,可以使用 ccache --clear。
Xcode 特定设置
为了确保 ccache 能在 iOS 和 Xcode 中正常工作,你需要在 ios/Podfile 中启用 React Native 对 ccache 的支持。
在你的编辑器中打开 ios/Podfile,然后取消注释 ccache_enabled 行。
ruby post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false, # TODO: Uncomment the line below :ccache_enabled => true ) end
在 CI 上使用此方法
Ccache 在 macOS 上使用 /Users/$USER/Library/Caches/ccache 文件夹来存储缓存。因此,你可以在 CI 上保存和恢复相应的文件夹,以加速构建。
但是,有几点需要注意:
在 CI 上,我们建议进行完全干净的构建,以避免缓存中毒问题。如果你遵循前面段落中提到的方法,你应该能够并行构建 4 个不同的 ABI,并且很可能不需要在 CI 上使用 ccache。
ccache 依赖于时间戳来计算缓存命中。这在 CI 上效果不佳,因为文件会在每次 CI 运行时重新下载。为了解决这个问题,你需要使用 compiler_check content 选项,该选项依赖于哈希文件内容。
分布式缓存
与本地缓存类似,你可能需要考虑为原生构建使用分布式缓存。这对于进行频繁原生构建的大型组织来说可能特别有用。
我们建议使用 sccache 来实现此目的。有关如何设置和使用此工具的说明,请参阅 sccache 的分布式编译快速入门。