Claude Codeでフロントエンドパフォーマンス最適化|Lighthouse CI・バンドル分析・Core Web Vitals・React Profiler実践ガイド

Claude Codeでフロントエンドパフォーマンス最適化|Lighthouse CI・バンドル分析・Core Web Vitals・React Profiler実践ガイド AI開発

「Lighthouseスコアが低い」「ページ表示が遅い」——パフォーマンス改善は原因の特定から始まりますが、その分析と修正こそClaude Codeが得意とする領域です。Lighthouseのレポートを読み込ませれば改善箇所を即座に特定し、バンドル分析の結果から不要な依存を見つけ出し、React Profilerの出力から最適なメモ化戦略を提案してくれます。

この記事では、Claude Codeを使ったフロントエンドパフォーマンス最適化の具体的なワークフローを解説します。Lighthouse CI連携、バンドルサイズ削減、Core Web Vitals改善(LCP・CLS・INP)、コード分割、React Profilerとの連携まで、計測→分析→改善→検証のサイクルを回す実践方法をまとめました。

スポンサーリンク

Lighthouse CI × Claude Codeで計測と改善を自動化する

LighthouseをCLIで実行し、そのJSON結果をClaude Codeに読ませて改善提案を自動生成するワークフローです。

Lighthouseで計測してClaude Codeに解析させる
# Lighthouse CLIをインストール
npm install -g lighthouse @lhci/cli

# JSONレポートを生成
lighthouse http://localhost:3000 --output=json --output-path=./lh-report.json

# Claude Codeにレポートを読ませて改善提案させる
> /add lh-report.json
> このLighthouseレポートを解析してください。
> パフォーマンススコアが90未満の項目について、
> 具体的な改善コードを提示してください。

lighthouserc.jsでパフォーマンスゲートを設定する

lighthouserc.js(パフォーマンス基準の設定)
module.exports = {
  ci: {
    collect: {
      startServerCommand: "npm run start",
      url: ["http://localhost:3000/", "http://localhost:3000/products"],
      numberOfRuns: 3,
    },
    assert: {
      preset: "lighthouse:recommended",
      assertions: {
        "categories:performance": ["error", { minScore: 0.9 }],
        "categories:accessibility": ["error", { minScore: 1 }],
        "largest-contentful-paint": ["error", { maxNumericValue: 2000 }],
        "cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }],
        "interaction-to-next-paint": ["error", { maxNumericValue: 200 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};
GitHub ActionsでLighthouse CIを自動実行
name: Lighthouse CI
on: [push, pull_request]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci && npm run build
      - run: npm install -g @lhci/cli
      - run: lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
Lighthouse CIのassert機能を使うと、パフォーマンススコアが基準値を下回ったPRを自動でブロックできます。「いつの間にかスコアが下がっていた」という事態を防げます。

バンドルサイズの分析と削減

バンドルサイズが大きいとLCPが悪化し、モバイルでの体験が大幅に劣化します。Claude Codeにバンドル分析結果を読ませると、不要な依存や重複パッケージを自動で特定できます。

Next.jsでのバンドル分析

Next.js Bundle Analyzer設定
# @next/bundle-analyzer をインストール
npm install @next/bundle-analyzer

# next.config.ts に追加
const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({ /* 既存設定 */ });

# 実行(ブラウザでツリーマップが開く)
ANALYZE=true npm run build

Vite 8でのバンドル分析

vite.config.ts(バンドル可視化)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { visualizer } from "rollup-plugin-visualizer";

export default defineConfig({
  plugins: [
    react(),
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
      template: "treemap",
    }),
  ],
});
Claude Codeにバンドル分析を依頼するプロンプト
npm run build を実行し、ビルド出力のチャンクサイズを確認してください。

以下を分析して改善案を提示してください:
1. 50KBを超えるチャンクの一覧
2. tree-shakingが効いていないモジュール
3. dynamic importに移行できるモジュール
4. 軽量な代替ライブラリへの置換候補
   (例: moment.js→date-fns, lodash→lodash-es の部分import)
5. 重複してバンドルされているパッケージ
新しいnpmパッケージを追加する前にbundlephobia.comでサイズを確認する習慣をCLAUDE.mdに書いておくと、Claude Codeが大きなライブラリの追加時に警告してくれます。

Core Web Vitals 改善の実践

Core Web Vitalsは2024年3月にFID→INP(Interaction to Next Paint)に置き換えられ、インタラクション応答速度がランキングシグナルとして重要性を増しています。

指標 Good Needs Improvement Poor
LCP(Largest Contentful Paint) ≤ 2.5秒 2.5〜4.0秒 > 4.0秒
INP(Interaction to Next Paint) ≤ 200ms 200〜500ms > 500ms
CLS(Cumulative Layout Shift) ≤ 0.1 0.1〜0.25 > 0.25

LCP改善 ── 画像最適化とプリロード

LCP改善プロンプト
このページのLCPが3.2秒です。改善してください。

確認すべき項目:
1. LCP要素(通常はヒーロー画像)を特定
2. 画像フォーマットをAVIF/WebPに変換
3. fetchpriority="high" を付与
4. lazy loading がLCP画像に付いていれば削除
5. link rel="preload" でLCP画像をプリロード
6. next/image を使っている場合はpriority属性を追加
LCP画像の最適パターン(HTML)
<!-- プリロードでLCP画像を先行取得 -->
<link rel="preload" as="image" href="/hero.avif" type="image/avif">

<!-- AVIF > WebP > JPEG のフォールバック -->
<picture>
  <source srcset="/hero.avif" type="image/avif">
  <source srcset="/hero.webp" type="image/webp">
  <img src="/hero.jpg" alt="Hero" width="1200" height="600"
       fetchpriority="high" decoding="async">
</picture>
LCP要素にloading="lazy"を付けるのはNGです。ファーストビューに表示される画像はlazy loadingをせず、fetchpriority="high"で優先取得させてください。

CLS改善 ── レイアウトシフトの防止

CLS防止のCSS
/* 画像・動画: width/height属性を指定すればブラウザが自動でaspect-ratioを計算する */
/* HTML側: <img src="..." width="1200" height="600"> を必ず指定 */
img, video {
  max-width: 100%;
  height: auto;
}

/* Webフォントのレイアウトシフト防止 */
@font-face {
  font-family: "CustomFont";
  src: url("/font.woff2") format("woff2");
  font-display: optional;  /* swapよりシフトが少ない */
  size-adjust: 100%;
}

/* 広告・動的コンテンツの最小サイズ確保 */
.ad-slot { min-height: 250px; }
.skeleton { aspect-ratio: 16 / 9; background: #f0f0f0; }

INP改善 ── インタラクション応答速度の最適化

INPはユーザーの操作(クリック・タップ・キー入力)から画面が更新されるまでの時間を計測します。長いJavaScriptタスクがメインスレッドをブロックすると悪化します。

scheduler.yield()でロングタスクを分割(Chrome 129+対応、Safari/Firefoxは未対応)
// 長い処理を50件ごとに分割してメインスレッドに制御を返す
async function processLargeList(items: Item[]) {
  for (let i = 0; i < items.length; i++) {
    processItem(items[i]);
    if (i % 50 === 0) {
      await scheduler.yield(); // メインスレッドに制御を返す
    }
  }
}

// scheduler.yield()未対応ブラウザ用のポリフィル
function yieldToMain(): Promise<void> {
  if ("scheduler" in globalThis && "yield" in scheduler) {
    return scheduler.yield();
  }
  return new Promise((resolve) => setTimeout(resolve, 0));
}
useTransitionでUI更新の優先度を分ける
import { useState, useTransition } from "react";

function SearchResults({ items }: { items: Item[] }) {
  const [query, setQuery] = useState("");
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    // 入力欄の更新は即座に反映(高優先度)
    setQuery(e.target.value);
    // 検索結果のフィルタリングは低優先度(INP悪化を防ぐ)
    startTransition(() => {
      setFiltered(items.filter((item) => item.name.includes(e.target.value)));
    });
  }

  return (
    <>
      <input value={query} onChange={handleChange} />
      {isPending ? <div>検索中...</div> : <ResultList items={filtered} />}
    </>
  );
}

コード分割とLazy Loadingの自動化

コード分割プロンプト
このプロジェクトのルーティング構成を確認し、
初期バンドルに含まれている全ページコンポーネントを特定してください。
各ページをReact.lazy + Suspenseでコード分割し、
ローディングUI(Skeleton)とErrorBoundaryも追加してください。
Claude Codeが生成するコード分割
import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";
import { ErrorBoundary } from "./ErrorBoundary";
import { PageSkeleton } from "./PageSkeleton";

// ルートベースのコード分割(各ページが別チャンクに分離される)
const Home = lazy(() => import("./pages/Home"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
const UserProfile = lazy(() => import("./pages/UserProfile"));

export function AppRoutes() {
  return (
    <ErrorBoundary fallback={<div>エラーが発生しました</div>}>
      <Suspense fallback={<PageSkeleton />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
          <Route path="/users/:id" element={<UserProfile />} />
        </Routes>
      </Suspense>
    </ErrorBoundary>
  );
}
Next.jsの場合はApp Routerがルートベースの自動コード分割を行うため、明示的なReact.lazyは不要です。重いコンポーネント(チャートライブラリ等)のみnext/dynamicで分割してください。

React Profiler × Claude Codeで再レンダリングを最適化する

React DevToolsのProfilerで計測した結果をClaude Codeに分析させると、useMemo/useCallback/React.memoの適用判断を自動化できます。

React Profiler分析プロンプト
React DevTools ProfilerでFlameGraphを確認しました。
再レンダリングコストが高いコンポーネントのTop5は:

1. ProductList: 45ms(propsは変更なし)
2. Chart: 32ms(dataプロップ変更なし)
3. Sidebar: 28ms(親の再レンダリングで連鎖)
4. CommentSection: 22ms(新しいオブジェクトリテラルをpropsに渡している)
5. Footer: 18ms(毎回再レンダリング)

各コンポーネントについて:
- React.memo が有効か(props比較コスト vs レンダリングコスト)
- useMemo/useCallback で防げる不要な再レンダリングがあるか
- コンポーネント分割で解決すべきか
判断基準と具体的な修正コードを提示してください。
メモ化の判断基準: actualDuration > 16ms(60fpsの1フレーム)かつprops未変更のコンポーネントがReact.memoの候補です。ただしbaseDurationとactualDurationが近い場合はすでに高速なため、メモ化のコスト(比較処理)が上回る可能性があります。「計測してから最適化」が鉄則です。

CLAUDE.mdにパフォーマンスバジェットを組み込む

Claude Codeにパフォーマンス意識を「常に」持たせるには、CLAUDE.mdにパフォーマンスバジェットを明記します。

CLAUDE.md(パフォーマンス基準)
## パフォーマンスバジェット

### Core Web Vitals 目標
- LCP: ≤ 2.5s(モバイル) / ≤ 2.0s(デスクトップ)
- INP: ≤ 150ms
- CLS: ≤ 0.05

### バンドルサイズ上限
- 初期JS: ≤ 200KB (gzip)
- 個別チャンク: ≤ 50KB (gzip)
- 画像(LCP要素): ≤ 100KB

### コーディングルール
- 新しいnpm依存追加前にbundlephobia.comでサイズを確認
- 50KB超のライブラリはdynamic importを検討
- LCP画像にlazy loadingを使わない。fetchpriority="high"を付与
- 画像はAVIF/WebP形式を優先
- リスト100件超はvirtualization(react-window等)を使用
- 重い処理はscheduler.yield()またはWeb Workerで分割
- React.memoは計測結果に基づいて適用(早すぎるメモ化は禁止)

### Lighthouseチェック
- Performanceスコア90以上を維持
- PRマージ前にlhci autorunで自動検証

CLAUDE.mdの詳しい書き方はCLAUDE.md完全ガイドをご覧ください。

よくある質問

QLighthouseスコアが80から上がりません。何から改善すべきですか?
AまずLCPを確認してください。多くの場合、ヒーロー画像の最適化(AVIF/WebP変換+preload+fetchpriority=”high”)だけでスコアが大きく改善します。次にバンドルサイズを確認し、50KB超のチャンクがあればdynamic importで分割します。Claude Codeに/add lh-report.jsonでレポートを渡すと、最もインパクトの大きい改善箇所を優先度順に提案してくれます。
QuseMemoやReact.memoはどこにでも付けるべきですか?
Aいいえ。React ProfilerでactualDuration > 16msかつprops未変更のコンポーネントだけがReact.memoの候補です。軽いコンポーネント(数ms以下)にReact.memoを付けると、比較処理のコストが上回って逆に遅くなる場合があります。「計測してから最適化」が原則です。
QINPが200msを超えています。一番効果的な改善方法は?
Aイベントハンドラ内の重い処理をstartTransitionで低優先度に切り替えるのが最も即効性があります。startTransition(() => { heavyUpdate() })とするだけで、ユーザーの操作感が大きく改善します。さらに改善する場合はscheduler.yield()でロングタスクを分割してメインスレッドに制御を返しましょう。
QNext.jsを使っていますがバンドル分析ができません
Anpm install @next/bundle-analyzerでインストールし、next.config.tswithBundleAnalyzerをラップしてください。ANALYZE=true npm run buildで実行するとブラウザにツリーマップが表示されます。Claude Codeに「バンドル分析を実行して50KB超のモジュールを特定して」と依頼すると一括で対応できます。
QCLSが0.2あります。何が原因ですか?
A最も多い原因は画像やiframeにwidth/heightが指定されていないことです。imgタグにwidth/height属性を追加するか、CSSでaspect-ratioを指定してください。次に多いのはWebフォントの遅延読み込みによるテキストシフトです。font-display: optionalにすると、フォントが間に合わない場合はシステムフォントを維持し、シフトをゼロにできます。

まとめ

  • Lighthouse CI: JSONレポートをClaude Codeに読ませると改善箇所を自動特定。GitHub Actionsでスコア90未満をブロック
  • バンドル分析: Next.js/@next/bundle-analyzer、Vite 8/rollup-plugin-visualizerの結果からClaude Codeが不要な依存を検出
  • Core Web Vitals: LCP≤2.5s(画像最適化+preload)、INP≤200ms(useTransition+scheduler.yield)、CLS≤0.1(aspect-ratio+font-display)
  • コード分割: React.lazy + SuspenseまたはNext.js dynamicでルートベース分割
  • React Profiler: 再レンダリングコストをClaude Codeに分析させてメモ化戦略を自動判断
  • CLAUDE.md: パフォーマンスバジェットを記載して日常開発で常に意識させる

フロントエンド開発全般はClaude Codeフロントエンド開発ガイド、テスト戦略はClaude Codeテスト完全ガイド、GitHub Actionsとの連携はClaude Code GitHub Actions完全ガイドもあわせてご覧ください。