Advanced (更新: 2026/6/7)

バンドルサイズの犯人を特定する:rollup-plugin-visualizerで重い依存を見つける

バンドルが重い原因を可視化で特定する手順。rollup-plugin-visualizerとsource-map-explorerで巨大依存と重複を見つけ、削減前に犯人を名指しする方法を実例で解説。

バンドルサイズの犯人を特定する:rollup-plugin-visualizerで重い依存を見つける

「サイトが重い気がするから、JSを軽くしておいて」

そう頼まれて、僕は最初、適当に lodash を関数importに直したり、画像を圧縮したりしました。数字は1割くらい減った。でも体感はまったく変わらない。あとでちゃんと測ったら、ページの重さの半分以上を、誰も気づいていなかった日付ライブラリの「全ロケール入り」が食っていたんです。

軽量化の前に、まず犯人を名指しする。これをサボると、軽い部分をいくら磨いても徒労に終わります。健康診断の数値を見ずに、なんとなく野菜を増やすようなものです。今日はその「測って犯人を見つける」ところだけに絞って書きます。

この記事の要点

  • バンドルが重い原因は「測らないと分からない」。勘で削っても効かない場所をいじって終わる。
  • 可視化の主役は rollup-plugin-visualizer(Vite/Astro)と source-map-explorer(生成物を直接読む)の2つ。
  • 見るべき犯人は3種類。①初回表示に不要な巨大依存、②同じパッケージの複数バージョン(重複)、③1つの巨大chunk。
  • raw・gzip・brotliの3つの数字を区別する。体感に近いのは圧縮後(gzip/brotli)。
  • 犯人が分かったら、削る作業はコード分割tree shakingへ。この記事は特定までで一区切り。

そもそもバンドル分析って何を見るのか

バンドル分析は、ブラウザへ配るJavaScriptやCSSの中身を開けて、どのパッケージが何KBを占めているかを一覧にする作業です。荷物をまとめて発送する前に、箱を開けて「この本の塊が重いな」と中身ごとに重さを量るのに近いです。

最初に数字の種類だけ揃えておきます。混乱の元になるので。

数字意味いつ見るか
raw圧縮前のサイズ解析ツール上で大きな塊を探すとき
gzipgzip圧縮後のサイズ実際の配信に近い、体感の目安
brotlibrotli圧縮後のサイズ最近のCDNでの実配信に最も近い

読者の体感に効くのは圧縮後(gzip/brotli)です。ただ「何が重いか」を探す段階では、rawの大きい塊から見たほうが犯人が目立ちます。圧縮後だと、繰り返しの多いコードが小さく見えて埋もれることがあるからです。両方並べて見るのがコツです。

順番も先に決めておきます。測る → 犯人を特定する → 削る → 再測定。この記事は前半2つ、つまり「測る」と「特定する」に集中します。削る具体策は後ろで内部リンクに送ります。

まず本番ビルドで測る(dev buildで判断しない)

一番やりがちな事故が、開発サーバーの数字で判断することです。dev buildは未圧縮でソースマップ込み、ホットリロード用のコードも混ざっていて、本番より太って見えます。必ず production build で測ります。

Viteなら、ビルドするだけで各chunkのサイズとgzipが出ます。

npm run build

出力はこんな形になります(Vite公式のBuilding for Productionに載っている通り、デフォルトでgzipまで表示されます)。

dist/assets/index-a1b2c3.js   182.41 kB │ gzip: 58.22 kB
dist/assets/vendor-d4e5f6.js  412.08 kB │ gzip: 131.74 kB

ここで「--analyze フラグは?」と探す人がいますが、Viteに vite build --analyze のような専用フラグはありません。Next.jsの一部バージョンには解析オプションがありますが、フレームワークごとに違うので、まずは公式の手順を確認してください。Viteでの本格的な可視化は、次に紹介するプラグインを足して行います。

この素のビルド出力で「vendorが重いな」くらいの当たりはつきます。でも、vendorの中の「誰が」重いのかは、これだけでは分かりません。中身を開ける道具が要ります。

rollup-plugin-visualizerで中身を地図にする

Vite(とAstro。Astroも内部はViteです)で一番手軽なのが rollup-plugin-visualizer です。ビルド時にHTMLのレポートを吐き、treemap(面積で大きさを表す地図)で「どの依存が広い面積を占めているか」を一目で見せてくれます。

npm install --save-dev rollup-plugin-visualizer

Viteの設定はこうです。gzipSizebrotliSize を有効にして、圧縮後の数字も一緒に出します。

// vite.config.ts
import { defineConfig } from "vite";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  plugins: [
    visualizer({
      filename: "dist/bundle-stats.html", // 出力先
      template: "treemap",                // 面積で大きさを見る地図
      gzipSize: true,                     // gzip後のサイズも出す
      brotliSize: true,                   // brotli後のサイズも出す
      open: false                         // CIで止まらないよう自動で開かない
    })
  ],
  build: {
    sourcemap: true // source-map-explorerでも使うので有効化
  }
});

Astroなら vite キーの中に同じプラグインを置きます。

// astro.config.mjs
import { defineConfig } from "astro/config";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  vite: {
    plugins: [
      visualizer({
        filename: "dist/bundle-stats.html",
        template: "treemap",
        gzipSize: true,
        brotliSize: true
      })
    ],
    build: { sourcemap: true }
  }
});

ビルドして、出てきたHTMLを開きます。

npm run build

レポートの開き方はOSで違います。Windowsは start dist/bundle-stats.html、macOSは open dist/bundle-stats.html、Linuxは xdg-open dist/bundle-stats.html です。

template は treemap 以外にも sunburstnetworkflamegraphlistraw-data などが選べます。最初は面積で直感的に分かる treemap、依存の「入れ子の深さ」まで追いたいときは sunburst が読みやすいです。地図を開いたら、まず大きな面積のブロックを上から順に疑ってください。チャート、リッチエディタ、地図、Markdown変換、日付ライブラリあたりが、初回表示に不要なのに居座っている定番の犯人です。

source-map-explorerで「実体」を確かめる

visualizerが「地図」なら、source-map-explorer は「実測」です。実際に生成されたJSファイルとソースマップを突き合わせて、どの行がどのパッケージ由来かまで割り出します。プラグインを足さず、ビルド済みの成果物に対して後から走らせられるのが利点です。

npm install --save-dev source-map-explorer
npm run build
npx source-map-explorer "dist/assets/*.js" --html dist/source-map-report.html

sourcemap: true でビルドしていれば、これで dist/source-map-report.html に内訳が出ます。visualizerの面積で目星をつけ、source-map-explorerで「本当にそのパッケージか」を裏取りする。2つを併用すると、思い込みで間違った犯人を吊るし上げる事故が減ります。

注意が1つ。ソースマップを本番の公開CDNに置きっぱなしにしないでください。内部実装が丸見えになります。分析が終わったら消すか、配信対象から外す設定にします。

重複(同じパッケージの複数バージョン)を見つける

巨大な単独依存と並んで多いのが「重複」です。見た目は小さいのに、同じライブラリが複数バージョン入っていて、合計で効いている。date-fns の2系と3系が混在する、lodashlodash-es が両方入る、UIライブラリが内部で別バージョンを連れてくる——よくあるパターンです。

依存ツリーを直接覗くのが確実です。

# npmの場合:どのバージョンがどこから来ているか
npm ls date-fns lodash lodash-es

# 重複を整理してみる(lockfileが更新される)
npm dedupe

pnpmなら why が分かりやすいです。

pnpm why date-fns
pnpm dedupe

npm ls の出力でバージョンが2行以上ぶら下がっていたら、それが重複の証拠です。visualizerのレポートで同じ名前のブロックが2つ見えるときも同じ。どこかのパッケージが古いバージョンを要求していることが多いので、npm ls で「誰が要求しているか」を辿るのがコツです。

犯人パターンを整理しておきます。これは「特定」のための早見表で、対策の深掘りはリンク先に譲ります。

犯人の見え方どう特定するか削る作業の送り先
巨大な単独依存(地図で広い)visualizer + source-map-explorertree shaking
初回表示に不要なUI(エディタ/チャート)treemapで面積を確認コード分割
同じ依存の複数版(重複)npm ls / pnpm whynpm dedupe + lockfile更新
1つの巨大chunkViteのビルド出力のgzip値コード分割
全ロケール入りの国際化データsource-map-explorerで内訳必要ロケールだけimport

Claude Codeに「特定まで」をやらせる

ここまでをClaude Codeに任せると、毎回手で開かずに済みます。ポイントは、いきなり「軽くして」と言わないこと。測定と特定までで一度止めて、報告させる。削る判断は人間が見てからにします。

このリポジトリの本番バンドルを分析して、重い原因を特定してください。削減はまだしないでください。

1. npm run build を実行し、dist/bundle-stats.html と source-map-explorer のレポートを生成する
2. gzipサイズの大きい依存を上位10件、表にする(raw / gzip 両方)
3. npm ls と pnpm why で、複数バージョンが入っている重複パッケージを洗い出す
4. それぞれを「初回表示に不要そう」「重複」「巨大な単独依存」に分類する
5. 各候補について、削るとしたらコード分割向きか tree shaking 向きかの当たりだけ書く
6. UI・SEO本文・CTA・計測イベントには手を触れず、最後にレポートのパスをまとめる

この頼み方だと、Claude Codeが暴走して関係ないファイルまで書き換える事故を防げます。出てきた表を見て、僕が「これは消していい」「これは残す」を決め、削る作業だけ別のタスクに切り出します。

僕がやらかした失敗3つ

正直に書きます。最初の分析は、的外れだらけでした。

ひとつ目は、dev buildの数字で犯人を決めたこと。開発サーバーのNetworkタブを見て「reactが重い」と勘違いし、いらない調整に半日溶かしました。本番ビルドで測り直したら、reactは想定内で、本当の犯人は別の地図ライブラリでした。

ふたつ目は、圧縮後の数字だけを見たこと。gzip後で見ると、繰り返しの多い自動生成コードが小さく見えて、犯人が埋もれていました。rawも並べて見たら、巨大な塊がすぐ浮かびました。両方見る、これに尽きます。

みっつ目は、重複を見落としたこと。地図上では小さく見えた date-fns が、実は2バージョン入っていて、合計するとそこそこ効いていました。npm ls を一度回すだけで分かったのに、面積だけ見て満足していたんです。それ以来、巨大依存と重複は必ずセットで確認しています。

よくある質問

Q. rollup-plugin-visualizer と source-map-explorer、どっちを使えばいい? 両方です。visualizerはビルド時に地図を出して全体の当たりをつけるのに向き、source-map-explorerはビルド済みの成果物に後から走らせて実体を裏取りするのに向きます。まずvisualizerで面積を見て、怪しい依存をsource-map-explorerで確認、が早いです。

Q. vite build --analyze で分析できると聞いたのですが? Viteにそのフラグはありません。Viteは素のビルドで各chunkのgzipサイズを出してくれるので大まかな当たりはつきますが、中身の内訳まで見るには rollup-plugin-visualizer を足します。フレームワークごとに事情が違うので公式手順を確認してください。

Q. raw・gzip・brotli、結局どれを基準にすればいい? 読者の体感はgzip/brotli(圧縮後)が近いです。ただ犯人を探す段階ではrawの大きい塊から疑うと見つけやすいです。判断は圧縮後、捜索はraw、と使い分けると迷いません。

Q. treemap以外のtemplateは使う場面ある? 依存の入れ子の深さまで追いたいときはsunburstが読みやすいです。CIのログに数字だけ残したいなら listraw-data も選べます。まずはtreemapで十分です。

Q. 重複を見つけたら、必ずdedupeすればいい? まず npm ls で「どのパッケージが古いバージョンを要求しているか」を確認してください。dedupeで揃うこともありますが、ライブラリ側のpeer依存の都合で残ることもあります。原因元を直すほうが根本解決になります。

実際に試した結果

このサンプルは、Masaの手元でVite/React想定の構成に落とし込み、rollup-plugin-visualizer のHTML出力、source-map-explorer のコマンド、npm ls での重複確認まで実際に動かして確認しました。一番効いたのは、削る前に「巨大な単独依存」「初回表示に不要なUI」「重複」の3つに必ず分類してから動いたことです。

特に、地図の面積だけ見ていると重複を取りこぼします。visualizerとsource-map-explorerと npm ls、この3点セットで初めて犯人が確定する、というのが実感でした。犯人さえ名指しできれば、あとの削減はコード分割tree shaking、全体設計はパフォーマンス最適化に沿って淡々と進むだけです。測らずに削る前の自分に、これを渡してやりたいです。

Claude Codeに分析の型を持たせたい人は、まず無料チートシートで頼み方を固めると早いです。チームでVite/Astro/Next.jsの既存アプリを診断し、CLAUDE.mdやレビュー基準まで整えたい場合はClaude Code研修・導入相談も使えます。

#バンドル分析 #rollup-plugin-visualizer #Vite #source-map-explorer #パフォーマンス
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。