Advanced

Bundle Analysis and Optimization with Claude Code

Learn about bundle analysis and optimization using Claude Code. Includes practical code examples.

バンドル分析でアプリの軽量化を実現

JavaScriptバンドルの肥大化は、ページの読み込み速度に直接影響します。Claude Codeを使えば、バンドル分析から最適化施策の実装まで効率的に進められます。

バンドルサイズの可視化

> バンドルサイズを分析して、最適化すべき依存関係を特定して。
> treemapで可視化する設定も追加して。
// vite.config.ts
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      filename: 'dist/bundle-stats.html',
      open: true,
      gzipSize: true,
      brotliSize: true,
      template: 'treemap',
    }),
  ],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        },
      },
    },
  },
});

バンドルサイズの自動チェック

// scripts/check-bundle-size.ts
import { readFileSync, readdirSync, statSync } from 'fs';
import path from 'path';
import { gzipSync, brotliCompressSync } from 'zlib';

interface BundleReport {
  file: string;
  raw: number;
  gzip: number;
  brotli: number;
}

function analyzeBundles(dir: string): BundleReport[] {
  const files = readdirSync(dir, { recursive: true }) as string[];
  
  return files
    .filter(f => /\.(js|css)$/.test(f))
    .map(file => {
      const filePath = path.join(dir, file);
      const content = readFileSync(filePath);
      return {
        file,
        raw: content.length,
        gzip: gzipSync(content).length,
        brotli: brotliCompressSync(content).length,
      };
    })
    .sort((a, b) => b.gzip - a.gzip);
}

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes}B`;
  return `${(bytes / 1024).toFixed(1)}kB`;
}

const reports = analyzeBundles('dist/assets');
console.log('\n📦 バンドルサイズレポート\n');
console.log('ファイル | Raw | Gzip | Brotli');
console.log('--- | --- | --- | ---');

let totalGzip = 0;
for (const r of reports) {
  totalGzip += r.gzip;
  console.log(`${r.file} | ${formatSize(r.raw)} | ${formatSize(r.gzip)} | ${formatSize(r.brotli)}`);
}

console.log(`\n合計 (gzip): ${formatSize(totalGzip)}`);

// サイズ上限チェック
const LIMIT = 200 * 1024; // 200kB
if (totalGzip > LIMIT) {
  console.error(`\n❌ バンドルサイズが上限(${formatSize(LIMIT)})を超えています!`);
  process.exit(1);
}

重い依存関係の特定と代替

> 大きな依存関係を軽量な代替に置き換える提案をして。
// よくある置き換えパターン
const replacements = {
  // moment.js (300kB) → dayjs (2kB)
  'moment': 'dayjs',
  
  // lodash (70kB) → lodash-es(tree-shaking対応)
  'lodash': 'lodash-es',
  
  // axios (13kB) → fetch API(組み込み)
  'axios': 'native fetch',

  // uuid (3.5kB) → crypto.randomUUID()(組み込み)
  'uuid': 'crypto.randomUUID()',
};

// lodashの個別インポート
// ❌ import _ from 'lodash';
// ✅ import debounce from 'lodash-es/debounce';

動的インポートによるコード分割

// ルートベースの分割
const routes = [
  {
    path: '/dashboard',
    component: lazy(() => import('./pages/Dashboard')),
  },
  {
    path: '/settings',
    component: lazy(() => import('./pages/Settings')),
  },
];

// 条件付きインポート
async function loadEditor() {
  const { EditorModule } = await import('./modules/heavy-editor');
  return new EditorModule();
}

// ライブラリの遅延読み込み
async function highlightCode(code: string, lang: string) {
  const { highlight } = await import('prismjs');
  return highlight(code, lang);
}

CI/CDでのバンドルサイズ監視

# .github/workflows/bundle-check.yml
name: Bundle Size Check
on: [pull_request]

jobs:
  bundle-size:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npx tsx scripts/check-bundle-size.ts
      - uses: actions/upload-artifact@v4
        with:
          name: bundle-report
          path: dist/bundle-stats.html

Summary

バンドル分析と最適化は、ツリーシェイキングと密接に関連しています。Claude Codeを使えば、依存関係の分析から軽量な代替への置き換えまで効率的に実施できます。CI/CDにバンドルサイズチェックを組み込むことで、パフォーマンスの劣化を未然に防げます。Viteのビルド最適化についてはVite公式ドキュメントを参照してください。

#Claude Code #bundle analysis #Webpack #Vite #performance