💡 Tips

Expo/React Nativeアプリのサイズ削減手順2025|Hermes・R8・アセット最適化で初回DL容量を削るチェックリスト

結論:Hermes → R8 → アセット → 依存の順に、毎段階で実測する

Expoアプリの容量削減で失敗する原因はほぼ一つに絞られる。思いつきの順で手を付けて、どれが効いたか分からなくなることだ。

効く順番は決まっている。

  1. 削減前のベースラインを実測する
  2. Hermes とネイティブ配布形式を確認する
  3. Android の R8 / ProGuard とリソース圧縮を入れる
  4. 画像・フォント・Lottie を最適化する
  5. 不要ライブラリを棚卸しする

各段階のあとに必ずビルドして数値を記録する。これをやらないと「削ったつもり」で終わる。

まず削減前の実測値を取る

比較すべきは「ビルド成果物のサイズ」ではなく「ユーザーの初回ダウンロードサイズ」だ。AABのファイルサイズと、Play配信時に端末が実際に落とすサイズは別物になる。

Androidは bundletool でダウンロードサイズを直接見られる。

bundletool build-apks --bundle=app.aab --output=app.apks
bundletool get-size total --apks=app.apks

iOSは Xcode Organizer からエクスポートしたときの App Thinning Size Report、または App Store Connect のアプリサイズ表示を使う。

JSバンドル側の内訳はこう見る。

npx expo export --platform android
du -sh dist

パッケージ単位の内訳が欲しいときは source-map-explorer や react-native-bundle-visualizer を併用する。エクスポート系のオプション名はSDKバージョンで変わるため、実行前に公式ドキュメントの最新情報を確認してほしい。

段階1:Hermes とネイティブ配布の確認

近年の Expo SDK では Hermes が既定エンジンだが、古いプロジェクトから移行したアプリではJSCのまま残っていることがある。

{ "expo": { "jsEngine": "hermes" } }

HermesではJSがバイトコード(.hbc)へ事前変換され、バンドル相当分のサイズと起動時間が下がる。ただしスタックトレースの扱いが変わるので、Sentry等へのソースマップアップロード運用を先に整えてから切り替えたほうが安全だ。

配布形式も合わせて確認する。PlayへAABを出していればABIごとの分割配信は自動で行われる。APKを直接配布している運用だけが、全ABI入りで膨らむ。

バンドルとネイティブ層の関係を体系的に押さえたい場合は

サンプルコードで作りながら学ぶReact Native実践入門 (技術の泉シリーズ(NextPublishing))
📚 おすすめ書籍

サンプルコードで作りながら学ぶReact Native実践入門 (技術の泉シリーズ(NextPublishing))

参考価格¥2,200(税込)

内部構造から理解したいときの一冊

Amazonで最新価格をチェック

のような書籍を1冊通すと、この後の判断が速くなる。

段階2:R8 / ProGuard とリソース圧縮(Android)

Managed workflowなら expo-build-properties で設定する。

["expo-build-properties", {
  "android": {
    "enableProguardInReleaseBuilds": true,
    "enableShrinkResourcesInReleaseBuilds": true
  }
}]

ここが「効くが最も壊れやすい」段階だ。リフレクションを使うSDKのクラスが削られると、リリースビルドだけ実行時に落ちる。keepルールを足し、リリース構成での実機QAを必ず通す。

-keep class com.example.model.** { *; }
-keepattributes Signature,*Annotation*

shrinkResourcesも同様で、文字列から動的に参照しているリソースは消える可能性がある。ここだけは「入れて終わり」にせず、主要導線を手で触って確認する。

段階3:アセット最適化

意外に大きいのはJSではなくアセットであることが多い。

対象よくある状態対処
PNG画像3x想定の大サイズをそのまま同梱WebP化・実表示サイズへリサイズ
日本語フォントフルセット同梱で数MBpyftsubsetでサブセット化、または端末標準フォント
Lottie高解像度ラスタ画像を内包したJSONベクター化・不要レイヤー削除
動画・音声初回起動に不要なものまで同梱リモート配信+キャッシュ

WebPはAndroidが標準対応、iOSも比較的新しいOSでは扱えるが、対応範囲は実機で確認してから全面移行する。

起動直後に必要ないアセットを同梱せず配信へ逃がすのが、単発では一番効く。ストレージ側の構成はCloudflare R2で画像アップロードを最安構成で実装する手順が参考になる。

段階4:不要ライブラリの棚卸し

npx depcheck
npx expo-doctor

削減余地が出やすいのは次の4つだ。

  • moment を使い続けている(dayjsやIntlへ置換)
  • lodash を名前空間ごとimportしている(個別importへ)
  • react-native-vector-icons で全アイコンフォントを同梱している(使うセットだけ登録)
  • 検証用に入れたまま残った expo-* モジュールやUIライブラリ

依存を1つ抜くたびにビルドして計測する。まとめて抜くと、壊れたときの切り分けができない。

削減前後の記録テンプレート

段階ごとに1行ずつ埋める。数値がない改善は続かない。

段階施策Android DLiOS DL起動時間実機QA
0ベースライン38.4MB62.1MB2.4s-
1Hermes + AAB配信29.1MB55.8MB1.9sOK
2R8 + shrinkResources26.3MB55.8MB1.9s要確認
3アセット最適化21.7MB47.0MB1.8sOK
4依存棚卸し18.9MB42.5MB1.7sOK

この数値は画面数40・依存60パッケージ規模のアプリでの一例にすぎない。構成によって効き方は大きく変わるので、絶対値ではなく「自分のアプリでどの段階が何MB効いたか」を残すことに意味がある。

注意点とトレードオフ

正直に書いておく。

  • R8は事故が起きる段階:難読化でリフレクション依存のコードが壊れる。削減幅の割にQAコストが高い
  • Hermes移行はデバッグ体験が変わる:ソースマップ運用が未整備だと本番調査が苦しくなる
  • アセット圧縮は品質劣化と背中合わせ:デザイナーと画質基準を合わせてから進める
  • 容量削減の効果は劇的ではない:一般に容量が小さいほどインストール完了率は上がるとされるが、数値効果はアプリと市場によって差が大きい。過度な期待はしない

ビルド設定のオプション名やSDKの既定値は更新が早い。着手前に Expo と Android Gradle Plugin の公式ドキュメントで最新仕様を確認してほしい。

実行チェックリスト

  • 削減前のAndroid DLサイズ・iOS DLサイズを記録した
  • jsEngine が hermes になっている
  • AAB(またはABI分割)で配布している
  • R8 と shrinkResources を有効化し、keepルールを追加した
  • リリース構成で主要導線の実機QAを通した
  • 画像をWebP化し、実表示サイズへリサイズした
  • フォントをサブセット化した
  • depcheck / expo-doctor で不要依存を除去した
  • 各段階の実測値を表に残した