ビルドフェーズの高速化
React Native アプリのビルドはコストが高く、開発者の時間を数分費やすことがあります。これは、プロジェクトの規模が大きくなるにつれて、また複数の React Native 開発者がいる大規模な組織では、一般的に問題となる可能性があります。
このパフォーマンスの低下を軽減するために、このページではビルド時間を改善するためのいくつかの提案を共有します。
これらの提案は、ネイティブのビルドツールがどのように機能するかについて、ある程度の理解を必要とする高度な機能であることにご注意ください。
開発中は1つのABIのみをビルドする (Androidのみ)
Androidアプリをローカルでビルドする場合、デフォルトでは、4つのApplication Binary Interfaces (ABIs)すべて、すなわち armeabi-v7a
、arm64-v8a
、x86
、およびx86_64
をビルドします。
しかし、ローカルでビルドしてエミュレータや物理デバイスでテストしている場合は、それらすべてをビルドする必要はないでしょう。
これにより、ネイティブビルド時間が約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-v8a
info Installing the app...
このメカニズムは、reactNativeArchitectures
Gradle プロパティに依存しています。
したがって、CLIを使用せずにコマンドラインから直接Gradleでビルドしている場合、ビルドしたい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 <task> -PreactNativeArchitectures=x86_64
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
アプリのリリースバージョンをビルドしたら、日常の開発ワークフローで使用しているABIだけでなく、すべてのABIで動作するapk/appバンドルをビルドしたいので、これらのフラグを削除することを忘れないでください。
構成キャッシュを有効にする (Androidのみ)
React Native 0.79以降、Gradle Configuration Cachingを有効にすることもできます。
yarn android
でAndroidビルドを実行すると、2つのステップからなるGradleビルドが実行されます (出典)
- 設定フェーズ。すべての
.gradle
ファイルが評価されます。 - 実行フェーズ。タスクが実際に実行され、Java/Kotlinコードがコンパイルされます。
これでConfiguration Cachingを有効にできるようになり、後続のビルドではConfigurationフェーズをスキップできるようになります。
これは、ネイティブコードに頻繁な変更を加える場合に、ビルド時間を改善するため有利です。
例えば、ネイティブコードの変更後にRN-Testerをより速く再構築する方法をここに示します。
Gradle Configuration Cachingを有効にするには、android/gradle.properties
ファイルに以下の行を追加します。
org.gradle.configuration-cache=true
Configuration Cachingに関する詳細なリソースについては、公式の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 enteprise repository
# that will be used exclusively to fetch all of your dependencies.
+exclusiveEnterpriseRepository=https://my.internal.proxy.net/
このプロパティを設定することで、ビルドは排他的に指定されたリポジトリから依存関係を取得し、他のリポジトリからは取得しません。
コンパイラキャッシュを使用する
頻繁にネイティブビルド(C++またはObjective-C)を実行している場合、コンパイラキャッシュを使用することでメリットが得られるかもしれません。
具体的には、ローカルコンパイラキャッシュと分散コンパイラキャッシュの2種類のキャッシュを使用できます。
ローカルキャッシュ
以下の手順はAndroidとiOSの両方で機能します。Androidアプリのみをビルドする場合は、これで問題ありません。iOSアプリもビルドする場合は、以下のXCode固有のセットアップセクションの手順に従ってください。
ネイティブビルドのコンパイルをキャッシュするには、ccacheの使用をおすすめします。CcacheはC++コンパイラをラップし、コンパイル結果を保存し、中間コンパイル結果が以前に保存されていればコンパイルをスキップすることで機能します。
Ccacheはほとんどのオペレーティングシステムのパッケージマネージャーで利用できます。macOSでは、brew install ccache
でccacheをインストールできます。または、公式のインストール手順に従ってソースからインストールすることもできます。
その後、2つのクリーンビルドを実行できます (例: Androidでは、まずyarn react-native run-android
を実行し、android/app/build
フォルダを削除してから、もう一度最初のコマンドを実行します)。2番目のビルドが最初のビルドよりもはるかに高速であったことに気づくでしょう (数分ではなく数秒かかるはずです)。ビルド中に、ccache
が正しく動作していることを確認し、キャッシュヒット/ミスの割合を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
を使用してリセットし、キャッシュヒット率を確認できます。
キャッシュをクリアする必要がある場合は、ccache --clear
で実行できます。
Xcode固有のセットアップ
ccache
がiOSおよびXcodeで正しく動作するようにするには、ios/Podfile
でccacheのReact Nativeサポートを有効にする必要があります。
エディタでios/Podfile
を開き、ccache_enabled
の行をコメント解除します。
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の分散コンパイルクイックスタートを参照してください。