アプリを軽量化する(不要機能の削除と Rust バイナリの最適化)

Cargo の release プロファイル(opt-level・lto・strip)でサイズ優先にし、使わないプラグインや tauri の機能、未使用のコマンドを外し、Vite の出力も絞って配布物を小さくする。

パフォーマンス 対象: Tauri 2.x 更新日: 読了目安: 約8分 perf-004
目次
  1. 前提条件
  2. 1. release プロファイルをサイズ優先にする (Rust)
  3. 2. 使っていない機能を外す
  4. プラグイン
  5. 未使用のコマンドをビルドから外す
  6. tauri とクレートの機能
  7. 3. フロントエンドの出力を絞る (TypeScript)
  8. 動作確認
  9. よくあるエラーと対処法
  10. OS ごとの違いと注意点
  11. 関連レシピ

Tauri アプリの配布物の大部分は Rust の実行ファイルで、フロントエンドもその中に圧縮して埋め込まれます。ここでは Cargo の release プロファイル、使っていないプラグインや機能、フロントエンドの出力を順に見直します。先に アプリのバンドルサイズを分析する でどこが大きいかを確かめ、画像が原因なら 画像を圧縮してサイズを減らす から手を付けます。

前提条件

プラグインや権限の追加はありません。編集するのは src-tauri/Cargo.toml、src-tauri/tauri.conf.json、vite.config.ts です。効果は npm run tauri build の出力どうしで比べます(本番用にアプリをビルドする)。

1. release プロファイルをサイズ優先にする (Rust)

Cargo の既定の release は実行速度を優先し、クレートをまたいだ最適化(LTO)も行いません。src-tauri/Cargo.toml に次を書きます。

[profile.release]
codegen-units = 1  # 並列コンパイルをやめ、最適化の余地を広げる
lto = true         # クレートをまたいで使われないコードを落とす
opt-level = "s"    # サイズ優先。"z" はさらに小さいが遅くなりやすい
panic = "abort"    # panic 時の巻き戻し処理を持たない
strip = true       # シンボル情報を実行ファイルに残さない

最小構成のアプリ(opener プラグインのみ、Windows x64、Tauri 2.11)の実行ファイルは次のように変わりました。

release プロファイル実行ファイル
Cargo の既定(上の設定なし)8.5 MB
上の設定で opt-level = 34.1 MB
上の設定(opt-level = "s")3.0 MB
上の設定で opt-level = "z"2.6 MB

引き換えになるものもあります。

  • 速度: "s" / "z" は計算の重い処理が遅くなることがあります。重いクレートだけ [profile.release.package.<クレート名>] で opt-level = 3 に戻せます。
  • ビルド時間: lto = true と codegen-units = 1 はビルドを長くします。lto = "thin" が中間です。
  • panic: panic = "abort" では、どこで起きた panic でもアプリがその場で終了します(パニック(クラッシュ)時の処理を書く)。

2. 使っていない機能を外す

プラグイン

tauri add が触った 4 か所をすべて戻します。src-tauri/Cargo.toml の依存、lib.rs の .plugin(...)、capabilities の権限、package.json の @tauri-apps/plugin-* です(プラグインをインストールして有効化する)。上のアプリから opener を外すと実行ファイルは約 160KB 減りました。

cd src-tauri
cargo remove tauri-plugin-opener
cd ..
npm uninstall @tauri-apps/plugin-opener

未使用のコマンドをビルドから外す

build.removeUnusedCommands を true にすると、tauri build のときに、capabilities でどこからも許可されていないプラグインのコマンドを取り除きます(tauri 2.4 以降)。core:default に含まれないウィンドウ操作などのコマンドも対象になるので効果が大きく、上のアプリでは約 600KB 減りました。

{
  "build": {
    "removeUnusedCommands": true
  }
}

ビルドの出力に「Removed unused commands from window: create, set_resizable, …」のように消えたコマンドが並びます。実行時に add_capability などで足した権限は考慮されないので、その権限でだけ使うコマンドも消えます。有効にしたら全機能を試します。

tauri とクレートの機能

実行時に capability を足さないなら、tauri の既定の機能のうち dynamic-acl を外せます。権限の定義一式を実行ファイルに持たなくなり、上のアプリでは約 180KB 減りました。既定の機能を切って残りを並べ直す書き方なので、tauri を更新したら既定の機能の一覧を確かめ直します。

[dependencies]
tauri = { version = "2", default-features = false, features = ["wry", "compression", "common-controls-v6", "x11", "dbus"] }

一方、依存クレートの機能を絞る効果は小さめです。使われないコードは LTO でほぼ落ちるので、tokio を features = ["full"] で足しても約 50KB でした。tauri.conf.json の dragDropEnabled: false のような設定も動作を切り替えるだけで、大きさは変わりません。

3. フロントエンドの出力を絞る (TypeScript)

Tauri が勧める Vite の設定では、tauri build --debug のときだけ最小化を止め、ソースマップを出します。sourcemap: true を固定で書いていると .map まで実行ファイルに埋め込まれるので、本番では出さないようにします。

// vite.config.ts
import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ mode }) => {
  // tauri dev / tauri build が渡す TAURI_ENV_* を読む
  const env = loadEnv(mode, '.', 'TAURI_ENV_');
  const debug = !!env.TAURI_ENV_DEBUG;
  return {
    build: {
      // WebView の版に合わせ、古いブラウザ向けの変換を入れない
      target: env.TAURI_ENV_PLATFORM === 'windows' ? 'chrome105' : 'safari13',
      minify: !debug,
      sourcemap: debug,
    },
  };
});

import() で分割しても、分けたファイルはすべて埋め込まれるので合計は減りません。減らすには大きなライブラリを軽いものに替えます。日本語の Web フォントは 1 ウェイトで数 MB になることもあり、OS のフォントで足りるなら同梱しないのが一番効きます。

動作確認

変更の前後で npm run tauri build を実行し、perf-001 のスクリプトで大きさを並べます。上のアプリでは次のようになりました。

状態実行ファイルNSIS インストーラー
1 章の表の opt-level = 34.1 MB1.3 MB
opt-level = "s"、opener の削除、removeUnusedCommands2.6 MB1.0 MB

小さくなったら、ボタンやメニューをひととおり操作して、消したプラグインやコマンドを呼んでいる箇所がないかを確かめます。

よくあるエラーと対処法

  • 「Permission opener:default not found, expected one of ...」: プラグインを外したのに capabilities に権限が残っています。その行を消します。
  • 「warning: profiles for the non root package will be ignored, specify profiles at the workspace root:」: ワークスペースのメンバーに [profile.release] を書いています。ルートの Cargo.toml へ移します。
  • 「The tauri dependency features on the Cargo.toml file does not match the allowlist defined under tauri.conf.json.」: protocol-asset のように設定と連動する機能を手で消し、cargo を直接実行しています。機能は assetProtocol.enable などの設定側で切り、tauri build で作り直すと Cargo.toml も合わせて書き換わります。
  • removeUnusedCommands の後で「Command … not found」という趣旨のエラー: そのコマンドを許可する権限が capabilities のファイルにありません。権限を足すか、設定を外します。

OS ごとの違いと注意点

  • Windows: bundle.windows.webviewInstallMode は既定の downloadBootstrapper のままが最小です。offlineInstaller(約 127MB 増)や fixedRuntime(約 180MB 増)は、ネットにつながらない環境へ配る場合だけにします。
  • macOS: universal-apple-darwin は両アーキテクチャのコードを持つので大きくなります。ダウンロードの大きさを優先するなら、アーキテクチャごとに分けて配ります。
  • Linux: AppImage は依存ライブラリを含むので 70MB を超えることがあります。大きさを気にするなら .deb / .rpm も用意します。
  • 実行ファイル圧縮ツール(UPX など)は、ウイルス対策ソフトの誤検知や macOS の署名の問題が起きやすいので勧めません。nightly の Rust でだけ使えるさらに進んだ設定は、公式の「アプリサイズの削減」にまとまっています。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する