Skip to main content

加快构建阶段

构建 React Native 应用可能成本高昂,并且需要开发者花费几分钟的时间。随着你的项目不断增长,并且通常在拥有多个 React Native 开发者的大型组织中,这可能会出现问题。

¥Building your React Native app could be expensive and take several minutes of developers time. This can be problematic as your project grows and generally in bigger organizations with multiple React Native developers.

为了减轻这种性能影响,本页面分享了一些有关如何缩短构建时间的建议。

¥To mitigate this performance hit, this page shares some suggestions on how to improve your build time.

信息

如果你发现 Android 上的新架构的构建时间较慢,我们建议升级到 React Native 0.71

¥If you're noticing slower build time with the New Architecture on Android, we recommend to upgrade to React Native 0.71

开发期间仅构建一个 ABI(仅限 Android)

¥Build only one ABI during development (Android-only)

在本地构建 Android 应用时,默认情况下会构建所有 4 个 应用二进制接口 (ABI)armeabi-v7aarm64-v8ax86x86_64

¥When building your android app locally, by default you build all the 4 Application Binary Interfaces (ABIs) : armeabi-v7a, arm64-v8a, x86 & x86_64.

但是,如果你在本地构建并测试模拟器或在物理设备上,则可能不需要构建所有这些。

¥However, you probably don't need to build all of them if you're building locally and testing your emulator or on a physical device.

这应该会将你的原生构建时间减少约 75%。

¥This should reduce your native build time by a ~75% factor.

如果你使用的是 React Native CLI,则可以将 --active-arch-only 标志添加到 run-android 命令中。此标志将确保从正在运行的模拟器或插入的手机中获取正确的 ABI。为了确认此方法工作正常,你将在控制台上看到类似 info Detected architectures arm64-v8a 的消息。

¥If you're using the React Native CLI, you can add the --active-arch-only flag to the run-android command. This flag will make sure the correct ABI is picked up from either the running emulator or the plugged in phone. To confirm that this approach is working fine, you'll see a message like info Detected architectures arm64-v8a on console.

$ 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-v8a
info Installing the app...

此机制依赖于 reactNativeArchitectures Gradle 属性。

¥This mechanism relies on the reactNativeArchitectures Gradle property.

因此,如果你直接从命令行使用 Gradle 进行构建而不使用 CLI,则可以指定要构建的 ABI,如下所示:

¥Therefore, if you're building directly with Gradle from the command line and without the CLI, you can specify the ABI you want to build as follows:

$ ./gradlew :app:assembleDebug -PreactNativeArchitectures=x86,x86_64

如果你希望在 CI 上构建 Android 应用并使用矩阵来并行构建不同架构,这会很有用。

¥This can be useful if you wish to build your Android App on a CI and use a matrix to parallelize the build of the different architectures.

如果你愿意,你还可以使用项目 顶层文件夹 中的 gradle.properties 文件在本地覆盖此值:

¥If you wish, you can also override this value locally, using the gradle.properties file you have in the top-level folder of your project:

# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
# ./gradlew <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64

一旦你构建了应用的发布版本,请不要忘记删除这些标志,因为你想要构建适用于所有 ABI 而不仅仅是你在日常开发工作流程中使用的 ABI 的 apk/应用包。

¥Once you build a release version of your app, don't forget to remove those flags as you want to build an apk/app bundle that works for all the ABIs and not only for the one you're using in your daily development workflow.

使用编译器缓存

¥Use a compiler cache

如果你经常运行原生构建(C++ 或 Objective-C),你可能会从使用编译器缓存中受益。

¥If you're running frequent native builds (either C++ or Objective-C), you might benefit from using a compiler cache.

具体来说,你可以使用两种类型的缓存:本地编译器缓存和分布式编译器缓存。

¥Specifically you can use two type of caches: local compiler caches and distributed compiler caches.

本地缓存

¥Local caches

信息

以下说明适用于 Android 和 iOS。如果你只构建 Android 应用,那么你应该可以开始了。如果你还构建 iOS 应用,请按照下面的 XCode 特定设置 部分中的说明进行操作。

¥The following instructions will work for both Android & iOS. If you're building only Android apps, you should be good to go. If you're building also iOS apps, please follow the instructions in the XCode Specific Setup section below.

我们建议使用 ccache 来缓存原生构建的编译。Ccache 的工作原理是封装 C++ 编译器,存储编译结果,如果最初存储了中间编译结果,则跳过编译。

¥We suggest to use ccache to cache the compilation of your native builds. Ccache works by wrapping the C++ compilers, storing the compilation results, and skipping the compilation if an intermediate compilation result was originally stored.

要安装它,你可以按照 官方安装说明

¥To install it, you can follow the official installation instructions.

在 macOS 上,我们可以使用 brew install ccache 安装 ccache。安装后,你可以按如下方式配置它来缓存 NDK 编译结果:

¥On macOS, we can install ccache with brew install ccache. Once installed you can configure it as follows to cache NDK compile results:

ln -s $(which ccache) /usr/local/bin/gcc
ln -s $(which ccache) /usr/local/bin/g++
ln -s $(which ccache) /usr/local/bin/cc
ln -s $(which ccache) /usr/local/bin/c++
ln -s $(which ccache) /usr/local/bin/clang
ln -s $(which ccache) /usr/local/bin/clang++

这将在 /usr/local/bin/ 内创建到 ccache 的符号链接,称为 gccg++ 等。

¥This will create symbolic links to ccache inside the /usr/local/bin/ which are called gcc, g++, and so on.

只要 $PATH 变量中 /usr/local/bin/ 先于 /usr/bin/(默认值),此操作就有效。

¥This works as long as /usr/local/bin/ comes first than /usr/bin/ inside your $PATH variable, which is the default.

你可以使用 which 命令验证它是否有效:

¥You can verify that it works using the which command:

$ which gcc
/usr/local/bin/gcc

如果结果是 /usr/local/bin/gcc,那么你实际上正在调用 ccache,它将封装 gcc 调用。

¥If the results is /usr/local/bin/gcc, then you're effectively calling ccache which will wrap the gcc calls.

提醒

请注意,ccache 的设置将影响你计算机上运行的所有编译,而不仅仅是与 React Native 相关的编译。需要你自担风险使用它。如果你无法安装/编译其他软件,这可能就是原因。如果是这种情况,你可以删除使用以下命令创建的符号链接:

¥Please note that this setup of ccache will affect all the compilations that you're running on your machine, not only those related to React Native. Use it at your own risk. If you're failing to install/compile other software, this might be the reason. If that is the case, you can remove the symlink you created with:

unlink /usr/local/bin/gcc
unlink /usr/local/bin/g++
unlink /usr/local/bin/cc
unlink /usr/local/bin/c++
unlink /usr/local/bin/clang
unlink /usr/local/bin/clang++

将你的计算机恢复到原始状态并使用默认编译器。

¥to revert your machine to the original status and use the default compilers.

然后,你可以进行两次干净的构建(例如,在 Android 上,你可以首先运行 yarn react-native run-android,删除 android/app/build 文件夹,然后再次运行第一个命令)。你会注意到第二个构建比第一个构建快得多(应该需要几秒钟而不是几分钟)。构建时,你可以验证 ccache 是否正常工作并检查缓存命中/未命中率 ccache -s

¥You can then do two clean builds (e.g. on Android you can first run yarn react-native run-android, delete the android/app/build folder and run the first command once more). You will notice that the second build was way faster than the first one (it should take seconds rather than minutes). While building, you can verify that ccache works correctly and check the cache hits/miss rate ccache -s

$ ccache -s
Summary:
Hits: 196 / 3068 (6.39 %)
Direct: 0 / 3068 (0.00 %)
Preprocessed: 196 / 3068 (6.39 %)
Misses: 2872
Direct: 3068
Preprocessed: 2872
Uncacheable: 1
Primary storage:
Hits: 196 / 6136 (3.19 %)
Misses: 5940
Cache size (GB): 0.60 / 20.00 (3.00 %)

请注意,ccache 汇总了所有构建的统计信息。你可以在构建之前使用 ccache --zero-stats 重置它们以验证缓存命中率。

¥Note that ccache aggregates the stats over all builds. You can use ccache --zero-stats to reset them before a build to verify the cache-hit ratio.

如果你需要擦除缓存,可以使用 ccache --clear 来执行此操作

¥Should you need to wipe your cache, you can do so with ccache --clear

XCode 特定设置

¥XCode Specific Setup

为了确保 ccache 在 iOS 和 XCode 上正常工作,你需要执行几个额外的步骤:

¥To make sure ccache works correctly with iOS and XCode, you need to follow a couple of extra steps:

  1. 你必须更改 Xcode 和 xcodebuild 调用编译器命令的方式。默认情况下,它们使用编译器二进制文件的完全指定路径,因此不会使用 /usr/local/bin 中安装的符号链接。你可以使用以下两个选项之一将 Xcode 配置为使用编译器的相对名称:

    ¥You must alter the way Xcode and xcodebuild call for the compiler command. By default they use fully specified paths to the compiler binaries, so the symbolic links installed in /usr/local/bin will not be used. You may configure Xcode to use relative names for the compilers using either of these two options:

  • 如果使用直接命令行,则在命令行上添加环境变量前缀:CLANG=clang CLANGPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ xcodebuild <rest of xcodebuild command line>

    ¥environment variables prefixed on the command line if you use a direct command line: CLANG=clang CLANGPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ xcodebuild <rest of xcodebuild command line>

  • ios/Podfile 中的 post_install 部分会在 pod install 步骤期间更改 Xcode 工作区中的编译器:

    ¥A post_install section in your ios/Podfile that alters the compiler in your Xcode workspace during the pod install step:

  post_install do |installer|
react_native_post_install(installer)

# ...possibly other post_install items here

installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# Using the un-qualified names means you can swap in different implementations, for example ccache
config.build_settings["CC"] = "clang"
config.build_settings["LD"] = "clang"
config.build_settings["CXX"] = "clang++"
config.build_settings["LDPLUSPLUS"] = "clang++"
end
end

__apply_Xcode_12_5_M1_post_install_workaround(installer)
end
  1. 你需要一个允许一定程度的草率和缓存行为的 ccache 配置,以便 ccache 在 Xcode 编译期间注册缓存命中。如果通过环境变量配置的话,与标准不同的 ccache 配置变量如下:

    ¥You need a ccache configuration that allows for a certain level of sloppiness and cache behavior such that ccache registers cache hits during Xcode compiles. The ccache configuration variables that are different from standard are as follows if configured by environment variable:

export CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
export CCACHE_FILECLONE=true
export CCACHE_DEPEND=true
export CCACHE_INODECACHE=true

可以在 ccache.conf 文件或 ccache 提供的任何其他机制中配置相同的内容。有关此内容的更多信息,请参阅 官方 ccache 手册

¥The same may be configured in a ccache.conf file or any other mechanism ccache provides. More on this can be found in the official ccache manual.

在 CI 上使用此方法

¥Using this approach on a CI

Ccache 使用 macOS 上的 /Users/$USER/Library/Caches/ccache 文件夹来存储缓存。因此,你也可以在 CI 上保存和恢复相应的文件夹,以加快构建速度。

¥Ccache uses the /Users/$USER/Library/Caches/ccache folder on macOS to store the cache. Therefore you could save & restore the corresponding folder also on CI to speedup your builds.

但是,有几点需要注意:

¥However, there are a couple of things to be aware:

  1. 在 CI 上,我们建议进行完全干净的构建,以避免缓存中毒问题。如果你遵循上一段中提到的方法,你应该能够在 4 个不同的 ABI 上并行化原生构建,并且你很可能不需要 CI 上的 ccache

    ¥On CI, we recommend to do a full clean build, to avoid poisoned cache problems. If you follow the approach mentioned in the previous paragraph, you should be able to parallelize the native build on 4 different ABIs and you will most likely not need ccache on CI.

  2. ccache 依赖时间戳来计算缓存命中。这在 CI 上效果不佳,因为每次 CI 运行时都会重新下载文件。为了克服这个问题,你需要使用 compiler_check content 选项,它依赖于 对文件内容进行哈希处理

    ¥ccache relies on timestamps to compute a cache hit. This doesn't work well on CI as files are re-downloaded at every CI run. To overcome this, you'll need to use the compiler_check content option which relies instead on hashing the content of the file.

分布式缓存

¥Distributed caches

与本地缓存类似,你可能需要考虑在原生构建中使用分布式缓存。这对于经常进行原生构建的大型组织特别有用。

¥Similar to local caches, you might want to consider using a distributed cache for your native builds. This could be specifically useful in bigger organizations that are doing frequent native builds.

我们建议使用 sccache 来实现此目的。我们遵循 sccache 分布式编译快速入门 以获取有关如何设置和使用此工具的说明。

¥We recommend to use sccache to achieve this. We defer to the sccache distributed compilation quickstart for instructions on how to setup and use this tool.