対象読者:フロントエンド開発で本番用JSを最適化したい人
ゴール:ビルドの再現性を保ちつつ、エラー調査しやすい sourcemap 付きの最小コードを出力する
なぜ minify するのか
- 転送量の削減:空白・改行・コメントを除去、識別子短縮でファイルサイズを大幅に縮小
- 描画の高速化:TTFB後のダウンロード完了が早まり、First Interaction も改善
- 難読化の副次効果:可読性が落ちるため、コードの“そのまま流用”を一定抑止(セキュリティ対策にはならない点に注意)
代表的な手段と選び方
手段 | 特徴 | 向き・不向き |
---|---|---|
esbuild(CLI) | 高速。--minify だけで完了。バンドルも得意 | 単体JS~中規模、WPテーマ等のシンプル構成 |
Terser(CLI / Rollup / Gulp) | きめ細かい圧縮設定。既存ツールチェーンに馴染む | 細かい最適化調整が必要なとき |
SWC(CLI/ツール) | Rust製で高速。Babelの代替としても | TypeScript変換と併用したい場合 |
Webpack(mode=production) | エコシステムが豊富。アセット連携が強い | 大規模SPA、複雑な依存管理 |
Vite(build) | ESMベース。デフォルトで圧縮 | SPA/MPAのモダン構成 |
本記事では esbuild と Terser を中心に、すぐ使える実例を示します。
esbuild でサッと最小構成
1) 単一ファイルをそのまま minify
npx esbuild path/to/app.js \
--minify \
--outfile=public/js/app.min.js
2) 依存をまとめて1本に(bundle)
npx esbuild path/to/app.js \
--bundle \
--minify \
--sourcemap=external \
--outfile=public/js/app.min.js
--sourcemap=external
:app.min.js.map
を別ファイルとして出力(本番での配信/秘匿を調整しやすい)- 改行・空白は
--minify
が除去します(= 改行なしの1行コードに)
3) 複数ファイルをまとめて出力(outdir)
npx esbuild src/**/*.js \
--minify \
--outdir=public/js \
--format=esm \
--sourcemap=external
4) ライセンス表記の扱い(著作権コメント)
デフォルトでは一部コメントが保持されます。完全に消すなら:
npx esbuild src/app.js \
--minify \
--legal-comments=none \
--outfile=public/js/app.min.js
※ ライセンス要件でヘッダー保持が必要な場合は --banner:js
で明示付与を。
5) npm scripts に定義(再現性確保)
{
"scripts": {
"build:js": "esbuild src/app.js --bundle --minify --sourcemap=external --outfile=public/js/app.min.js",
"watch:js": "esbuild src/app.js --bundle --minify --sourcemap --outfile=public/js/app.min.js --watch"
},
"devDependencies": {
"esbuild": "^0.24.0"
}
}
Terser を使う(細かい最適化が必要なとき)
1) 単体ファイルを minify
npx terser src/app.js \
--compress \
--mangle \
--output public/js/app.min.js \
--source-map "url='app.min.js.map'"
2) よく使うオプション
--compress
:デッドコード除去、条件式の簡略化などを有効化--mangle
:識別子の短縮(衝突が怖いときは--mangle reserved=['$','jQuery']
のように予約)--ecma 2020
:対象ECMAScript指定で最適化が効きやすくなる場合あり--comments all|some|false
:著作権コメントの扱い
3) npm scripts 例
{
"scripts": {
"min:js": "terser src/app.js --compress --mangle --output public/js/app.min.js --source-map \"url='app.min.js.map'\""
},
"devDependencies": {
"terser": "^5.31.0"
}
}
Source Map(ソースマップ)の実務
置き場所
- もっとも簡単:minifiedと同じディレクトリに
*.map
を出す - どうしても別ディレクトリ
/maps
に置きたい場合は、出力後に移動してsourceMappingURL
を書き換えるのが現実的です(esbuild/Terserは“mapだけ別ディレクトリ”を素直に指定できないため)。
例:ビルド後に .map を移動して書き換え(macOS)
# 1) ビルド(mapは同じ場所に出す)
npx esbuild src/app.js --bundle --minify --sourcemap=external --outfile=public/js/app.min.js
# 2) mapを別ディレクトリに移動
mkdir -p public/maps
mv public/js/app.min.js.map public/maps/
# 3) 参照先を書き換え(sourceMappingURL を /maps に向ける)
# ※ sed の -i は macOS では拡張子要指定('' で回避)
sed -i '' 's|//# sourceMappingURL=app.min.js.map|//# sourceMappingURL=/maps/app.min.js.map|' public/js/app.min.js
- CDN配信するなら、
sourceMappingURL
を CDNの絶対URL にしておくとデバッグしやすい - 本番公開で map を配りたくない場合は、map非公開にして 500系で返す、もしくは
--sourcemap
自体をビルド環境でのみ有効にする
よくある落とし穴と対策
- 即時関数やグローバル衝突
ライブラリ同士がwindow.$
を奪い合うなど。IIFE
化やimport
/export
ベースにリファクタしてからバンドル。 - 副作用のあるモジュール除去
Tree Shaking が“副作用なし”と誤判定して削除することがある。package.json
のsideEffects
設定、あるいは keep 指定を検討。 - ライセンスコメントの削除
ライセンス継承が必要なコードを取り込んでいる場合、--legal-comments=external|inline
等で適切に保持する。 - 本番での sourcemap 漏洩
逆コンパイルされるため、アクセス制御や非公開運用を。セキュリティ情報が含まれないかも確認。 - 互換性(古いブラウザ)
最適化の結果、古い環境で壊れることがある。必要なら トランスパイル(Babel/SWC) と ターゲット指定(esbuildの--target=es2018
など)を併用。
そのまま使える小さなテンプレート
npm + esbuild(単体ファイルを高速最小化)
{
"name": "my-theme",
"private": true,
"scripts": {
"build": "esbuild src/app.js --bundle --minify --sourcemap=external --outfile=public/js/app.min.js",
"build:prod": "esbuild src/app.js --bundle --minify --legal-comments=none --sourcemap=external --outfile=public/js/app.min.js && mkdir -p public/maps && mv public/js/app.min.js.map public/maps/ && sed -i '' 's|//# sourceMappingURL=app.min.js.map|//# sourceMappingURL=/maps/app.min.js.map|' public/js/app.min.js"
},
"devDependencies": {
"esbuild": "^0.24.0"
}
}
npm + Terser(細かく調整したいとき)
{
"name": "my-theme",
"private": true,
"scripts": {
"min": "terser src/app.js --compress --mangle --output public/js/app.min.js --source-map \"url='/maps/app.min.js.map'\" && mkdir -p public/maps && mv public/js/app.min.js.map public/maps/"
},
"devDependencies": {
"terser": "^5.31.0"
}
}
コメント