Claude CodeでJavaScriptバンドル分析を自動化する実装ガイド
Vite/Astro/Nextのバンドル分析、重複依存、dynamic import、CI予算をClaude Codeで改善する実践ガイド。
JavaScriptのバンドルは、放っておくと少しずつ太ります。最初は軽かったLPや管理画面でも、グラフ、日付処理、リッチエディタ、認証SDK、A/Bテスト用コードを足していくうちに、初回表示で読者が必要としていない処理までまとめて届きます。
バンドル分析とは、ブラウザへ配るJavaScriptやCSSの中身を可視化し、どの依存パッケージが何KBを占めているかを確認する作業です。初心者向けに言い換えると「荷物の重さを量り、何が重いかを箱ごとに開けて見る」作業です。Claude Codeを使う場合も、いきなり「軽くして」と頼むのではなく、測定、原因特定、修正、検証、CIの順に進めます。
この記事では、Vite、Astro、Next.js系のプロジェクトで使える実装手順をまとめます。rollup-plugin-visualizerやsource-map-explorerで可視化し、重複パッケージを見つけ、dynamic importで遅延読み込みし、CIでバンドル予算を守るところまで扱います。関連する背景はコード分割、ツリーシェイキング、パフォーマンス最適化も合わせて読むと理解しやすいです。
まず全体像をそろえる
バンドル分析で見る数字は、主にraw、gzip、brotliの3つです。rawは圧縮前のサイズ、gzipとbrotliは配信時に近い圧縮後サイズです。ユーザー体験に近いのは圧縮後ですが、解析ツール上の大きな塊を読むときはrawも役に立ちます。
次の流れを1セットにすると、Claude Codeの作業が安定します。
flowchart LR
A["production build"] --> B["visual report"]
B --> C["duplicate packages"]
C --> D["replace or dedupe"]
B --> E["route-level split"]
D --> F["bundle budget in CI"]
E --> F
F --> G["Claude Code review"]
公式ドキュメントは必ず根拠にします。ViteはBuilding for Production、AstroはAnalyze bundle size、Next.jsはPackage Bundling、予算設計はweb.devのPerformance budgets 101を確認してください。Claude Code自体の使い方はAnthropic公式ドキュメントを基準にします。
ViteとAstroで可視化する
ViteやAstroでは、まずrollup-plugin-visualizerを入れるのが簡単です。Astroも内部でViteを使うため、vite.pluginsに同じ考え方を入れられます。
npm install -D rollup-plugin-visualizer
// 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,
brotliSize: true,
open: false
})
],
build: {
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
react: ["react", "react-dom"],
charts: ["recharts"],
editor: ["@tiptap/react", "@tiptap/starter-kit"]
}
}
}
}
});
Astroなら次の形です。
// 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
}
}
});
実行します。
npm run build
start dist/bundle-stats.html
macOSならopen dist/bundle-stats.html、Linuxならxdg-open dist/bundle-stats.htmlです。レポートでは、初回表示に不要なチャート、エディタ、地図、動画、Markdown変換、日付ライブラリを探します。
source-map-explorerで実体を読む
source-map-explorerは、生成されたJSとsource mapを使って中身を確認するツールです。Viteではsourcemap: trueを有効にしてから実行します。
npm install -D source-map-explorer
npm run build
npx source-map-explorer "dist/assets/*.js" --html dist/source-map-report.html
Next.jsでは、現在の構成に合わせて公式のバンドル解析機能か@next/bundle-analyzerを使います。webpack前提の古い記事をそのまま信じるのではなく、Next.jsのバージョンとApp Router/Pages Routerの違いを先に確認してください。
// next.config.mjs
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true"
});
export default withBundleAnalyzer({
reactStrictMode: true
});
npm install -D @next/bundle-analyzer
ANALYZE=true npm run build
Windows PowerShellでは環境変数の書き方が違います。
$env:ANALYZE="true"; npm run build
重複パッケージを見つける
バンドルが重い原因は「大きいライブラリ」だけではありません。同じパッケージが複数バージョン入っていると、見た目以上に効きます。例えばdate-fnsが2系と3系で混在する、lodashとlodash-esが両方入る、UIライブラリが内部で別バージョンの依存を持つ、といったケースです。
npm ls date-fns lodash lodash-es
npm dedupe
pnpmなら次を使います。
pnpm why date-fns
pnpm dedupe
Claude Codeには、次のように依頼します。
このリポジトリの本番バンドルを分析してください。
1. dist/bundle-stats.html または source-map-explorer の結果から重い依存を表にする
2. npm ls / pnpm why で重複パッケージを確認する
3. 置き換え候補、dynamic import候補、削除してよい候補を分ける
4. 既存のUI、SEO本文、CTA、計測イベントを壊さない前提で最小差分を提案する
5. npm run build とバンドル予算チェックまで実行できる形にする
置き換えは慎重に行います。
| よくある原因 | 対策 | 注意点 |
|---|---|---|
momentが全ページに入る | date-fnsやIntl.DateTimeFormatへ寄せる | タイムゾーン処理を確認する |
lodash全体import | 関数単位のimportへ変える | CommonJS版とESM版を混ぜない |
| 管理画面用エディタ | dynamic importでクリック後に読む | 初回クリックが遅すぎないか見る |
| チャートライブラリ | レポート画面だけに分離 | SSRやレスポンシブ崩れを確認する |
| 同じ依存の複数版 | lockfile更新とdedupe | ライブラリ側のpeer依存を確認する |
dynamic importで初回表示から外す
dynamic importは、必要になった時点でモジュールを読み込む仕組みです。初心者向けに言えば、全部の工具を玄関に置くのではなく、作業場に入ったときだけ大きな工具箱を出す方法です。
Reactの例です。
// src/features/reports/ReportsButton.tsx
import { useState } from "react";
export function ReportsButton() {
const [html, setHtml] = useState<string>("");
const [loading, setLoading] = useState(false);
async function handleClick() {
setLoading(true);
const { renderRevenueReport } = await import("./renderRevenueReport");
setHtml(renderRevenueReport([12000, 18400, 9300]));
setLoading(false);
}
return (
<section>
<button type="button" onClick={handleClick} disabled={loading}>
{loading ? "レポートを生成中" : "収益レポートを表示"}
</button>
<output aria-live="polite">{html}</output>
</section>
);
}
// src/features/reports/renderRevenueReport.ts
export function renderRevenueReport(values: number[]): string {
const total = values.reduce((sum, value) => sum + value, 0);
return `今月の合計: ${new Intl.NumberFormat("ja-JP").format(total)}円`;
}
Next.jsのクライアント専用エディタなら、検索に必要な本文やCTAをサーバー側に残し、重い編集UIだけを遅延読み込みします。
// app/admin/EditorSlot.tsx
"use client";
import dynamic from "next/dynamic";
const RichEditor = dynamic(() => import("./RichEditor"), {
ssr: false,
loading: () => <p aria-live="polite">エディタを読み込み中...</p>
});
export function EditorSlot() {
return <RichEditor initialMarkdown="# Draft" />;
}
ssr: falseは便利ですが、SEO本文、価格、商品説明、相談CTAに使うと検索にも収益にも悪影響が出ます。管理画面、クリック後のモーダル、ブラウザAPI依存のエディタなどに限定します。
CIでバンドル予算を守る
一度軽くしても、次のPRで戻ります。予算をCIに入れて、増えた理由を書かないとマージできない状態にします。
// scripts/check-bundle-budget.mjs
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { brotliCompressSync, gzipSync } from "node:zlib";
const targetDir = "dist/assets";
const maxTotalGzip = 220 * 1024;
const maxSingleGzip = 140 * 1024;
function walk(dir) {
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return walk(fullPath);
return /\.(js|css)$/.test(entry.name) ? [fullPath] : [];
});
}
if (!existsSync(targetDir)) {
console.error(`Missing ${targetDir}. Run npm run build first.`);
process.exit(1);
}
const rows = walk(targetDir).map((file) => {
const content = readFileSync(file);
return {
file,
raw: statSync(file).size,
gzip: gzipSync(content).byteLength,
brotli: brotliCompressSync(content).byteLength
};
}).sort((a, b) => b.gzip - a.gzip);
console.table(rows.map((row) => ({
file: row.file,
rawKB: (row.raw / 1024).toFixed(1),
gzipKB: (row.gzip / 1024).toFixed(1),
brotliKB: (row.brotli / 1024).toFixed(1)
})));
const totalGzip = rows.reduce((sum, row) => sum + row.gzip, 0);
const tooLarge = rows.filter((row) => row.gzip > maxSingleGzip);
if (totalGzip > maxTotalGzip || tooLarge.length > 0) {
console.error(`Bundle budget failed. total gzip=${totalGzip} bytes`);
for (const row of tooLarge) {
console.error(`Large asset: ${row.file} gzip=${row.gzip} bytes`);
}
process.exit(1);
}
console.log(`Bundle budget passed. total gzip=${totalGzip} bytes`);
# .github/workflows/bundle-budget.yml
name: Bundle Budget
on:
pull_request:
jobs:
bundle-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: node scripts/check-bundle-budget.mjs
- uses: actions/upload-artifact@v4
if: always()
with:
name: bundle-stats
path: |
dist/bundle-stats.html
dist/source-map-report.html
最初の予算は理想値にしすぎないでください。現在値に10%程度の余白を置き、重いPRでは「なぜ増えたか」「どのページに効くか」「代替案はあるか」をレビューで確認するほうが運用に乗ります。
3つ以上の実例
1つ目は、SaaSの管理ダッシュボードです。一般ユーザーが最初に見る一覧、検索、CTAは軽く保ち、管理者だけが使うグラフ、CSV出力、監査ログを別chunkにします。Claude Codeには、src/features/adminだけを対象にして、ナビゲーションや認証処理を触らないよう指示します。
2つ目は、記事メディアや教材サイトです。本文、見出し、商品リンク、相談CTAは最初からHTMLに出し、Markdownプレビュー、画像トリミング、ABテスト管理画面だけ遅延読み込みします。広告やアフィリエイト導線があるページでは、速度改善が収益に直結します。計測はアナリティクス実装とつなげて見ると判断しやすいです。
3つ目は、LPに地図、動画、診断フォームを入れるケースです。ファーストビューでは価値提案、価格、CTAを出し、地図や動画プレイヤーはスクロール後またはクリック後に読みます。動画UIは動画プレイヤー実装、フォーム導線はアクセシビリティ実装も確認します。
4つ目は、社内UIライブラリです。@company/uiのトップレベルimportでボタンだけ使ったつもりが、DatePicker、Modal、Chartまで評価されることがあります。exportsを細かく分け、CSSやテーマ初期化の副作用をsideEffectsで明示します。
失敗例と落とし穴
一番多い失敗は、dev buildの数字で判断することです。必ずproduction buildで測ります。Viteならnpm run build、Next.jsならnext buildを使い、ローカル開発サーバーのNetworkタブだけで結論を出さないでください。
次に、細かく分けすぎる失敗です。chunkが増えすぎるとリクエスト数と待ち時間が増え、体感が悪くなります。「初回表示から外す価値がある大きな塊」だけを分けます。
3つ目は、source mapの扱いです。分析には便利ですが、公開設定を誤ると内部実装が見えすぎます。公開CDNにsource mapを置く方針は、監視ツール、エラー解析、セキュリティ方針と合わせて決めます。
4つ目は、manualChunksの固定しすぎです。最初は分かりやすくても、依存が変わると逆にキャッシュ効率や分割単位が悪くなります。設定を足したら、1か月後に消せるかもレビュー対象にします。
5つ目は、Claude Codeに修正だけ頼んで検証を任せないことです。差分を作った後に、バンドル表、重複依存、実画面、CTA、CI結果までレビューさせる必要があります。
このPRをバンドル分析の観点でレビューしてください。
- 初回表示に不要な依存が残っていないか
- 同じパッケージの複数バージョンがないか
- dynamic import後にローディングUIとエラー時の表示があるか
- SEO本文、価格、CTA、計測イベントが遅延読み込み対象に入っていないか
- bundle budgetの失敗時に、開発者が原因を追えるログになっているか
- npm run build の結果と生成されたレポートパスを最後にまとめてください
収益導線と学習導線
バンドル分析は、単なる技術整理ではありません。初回表示が軽くなると、記事本文、商品リンク、無料チートシート、相談CTAが早く見えます。ClaudeCodeLabのようなメディアでは、ここが広告収益、教材購入、導入相談の入口になります。
個人で始めるなら、まず無料チートシートでClaude Codeへの頼み方を固め、実装レビューの型を増やしたい場合は商品一覧のテンプレートを使うと早いです。チームでVite、Astro、Next.jsの既存アプリを診断し、CLAUDE.md、CI、レビュー基準、収益導線までまとめて整えたい場合はClaude Code研修・導入相談を検討してください。
実際に試した結果
この記事のサンプルは、Masaの手元でVite/React想定の構成に落とし込み、rollup-plugin-visualizerのHTML出力、source-map-explorerの実行コマンド、gzip/brotliを測るNodeスクリプトの構文を確認しました。実務で効いたのは、いきなり置き換えることではなく、最初に「初回表示から外してよいもの」と「SEOや収益導線として残すもの」を分けることでした。特にリッチエディタとチャートは削減幅が大きい一方、CTAや本文まで遅らせると逆効果になるため、人間が最後に画面とレポートを見て判断する必要があります。
まとめ
Claude Codeでバンドルを軽くするなら、測定なしの勘に頼らないことが第一です。Vite/Astroではrollup-plugin-visualizer、必要に応じてsource-map-explorer、Next.jsでは公式のバンドル解析を使い、重複依存、重い依存、初回表示に不要なUIを順に見ます。
最後はCIのバンドル予算とClaude Codeレビューで戻りを防ぎます。コード分割、ツリーシェイキング、バンドル分析は別々の技術ではなく、公開後の読者体験と収益導線を守る同じ運用の一部として扱うのが現実的です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Code Permission Receipt Pattern: 許可、証拠、ロールバックを残す運用
Claude Codeの権限運用を安全にする permission receipt。許可範囲、承認待ち、検証コマンド、CTA導線を記録します。
Claude CodeとCodex、結局どっち?事故らない“併用”の現実解
OpenAIのCodexとClaude Code、どっちが得意でどっちに任せる?両方を安全に併用する作業分担と権限・検証のワークフローを、僕の失敗談つきで解説します。
Claude Codeサブエージェント実装ガイド: 記事・コード作業を安全に並列委譲する方法
Claude Codeサブエージェントで記事・コード作業を安全に並列化する実装ガイド。委譲基準、プロンプト、失敗例を解説。