mcpField
E
engineering-frontend-developer

engineering-frontend-developer

github.com/peterhdd開発11242 回インストール33 週間前に更新
リポジトリ

engineering-frontend-developer について

React、Vue、Angular、Svelte を使う Web アプリやコンポーネントライブラリの開発者向けガイドです。レイアウト、パフォーマンス、アクセシビリティ、テスト、ビルドツール、レンダリング方式の判断基準を示します。

主な機能

  1. 01React、Vue、Angular、Svelte のガイド
  2. 02コンポーネント設計と TypeScript の規約
  3. 03パフォーマンス診断と最適化のルール
  4. 04アクセシビリティ実装とテストの指針
  5. 05ビルドツールと SSR、CSR、SSG の選択

活用シーン

  1. 01新規 Web アプリのフレームワークとツール選定
  2. 02レイアウト、性能、水和の問題の調査
  3. 03アクセシビリティを考慮したコンポーネントライブラリ開発

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/peterhdd/agent-skills --skill engineering-frontend-developer

インストール後、Claude Code で/engineering-frontend-developerを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
css-patterns.md
data-table.md
react-patterns.md
typescript-patterns.md
scripts/
check_bundle.sh

詳細

フロントエンド開発ガイド

概要

このガイドでは、React、Vue、Angular、Svelteを使った最新のフロントエンド開発について、コンポーネントアーキテクチャ、パフォーマンスの最適化、アクセシビリティ、テストを含めて説明します。Webアプリケーションやコンポーネントライブラリの構築、またはフロントエンドのパフォーマンス最適化に活用してください。

フレームワークとレイアウトの選択ルール

  • フレームワークを選ぶ際は、チームの専門知識とプロジェクトの制約に合わせてください。幅広いエコシステムが必要な場合はReact、既存ページへの段階的な導入にはVue、バンドルサイズが特に重要なアプリにはSvelte、組み込みのDIとルーティングを備えた、方針が明確なフルフレームワークが必要なプロジェクトにはAngularを標準として選びます。
  • デザインを実装する際は、2次元のページレイアウトにはCSS Grid、1次元のコンポーネントの配置にはFlexboxを使用してください。レスポンシブな再配置が崩れるため、レイアウト目的での絶対位置指定は避けてください。
  • コンポーネントライブラリを構築する際は、各コンポーネントを名前付きエクスポートとして公開し、TypeScriptのpropsインターフェース、Storybookのストーリー、単体テストを用意してください。この3つがすべて揃っていないコンポーネントはマージしません。
  • バックエンドAPIと連携する際は、型付けされたAPIクライアント層(例:fetchまたはaxiosとインターセプターを使用する単一のapi.tsモジュール)にfetch処理を集約し、認証ヘッダー、エラー変換、再試行を一元管理してください。

パフォーマンスの選択ルール

  • Lighthouse CIでページのLargest Contentful Paintが2.5秒を超えた場合は、マージを妨げるバグとして扱ってください。Chrome DevToolsのPerformanceタブでプロファイリングし、マージ前に最大のボトルネックを解消してください。
  • アニメーションを追加する際は、レイアウトの再計算によるカクつきを避けるため、width、height、top/leftではなく、(コンポジターのみで処理される)CSSのtransformとopacityプロパティを使用してください。
  • アプリにオフライン対応が必要な場合は、静的アセットにはキャッシュファースト戦略、APIリクエストにはネットワークファースト戦略を適用するサービスワーカーを登録し、オフライン時にはキャッシュ済みのレスポンスにフォールバックしてください。
  • 初期JSバンドルが予算(例:gzip圧縮後に200 KB)を超えた場合は、React.lazy()または動的なimport()を使ってルート単位のコード分割を追加し、重要度の低いスクリプトの読み込みを画面下部まで遅らせてください。
  • 古いブラウザーをサポートする場合は、browserslistの設定を定義し、ビルドツール(Vite、Webpack)で自動的にポリフィルを追加してください。browserslistの対象範囲では不十分な場合を除き、手動でポリフィルや機能チェックを追加しないでください。
  • 画像を追加する際は、レイアウトシフトを防ぐため、WebP/AVIFソースと明示的なwidth/height属性を指定した<picture>を使用してください。画面下部にある画像には、loading="lazy"を追加してください。
  • 初期ページ読み込み時に必要のないルートは、<Suspense>のフォールバックを指定してReact.lazy()(またはフレームワーク相当の機能)でラップし、そのルートのコードがメインバンドルに含まれないようにしてください。
  • 静的アセットを配信する際は、CDNまたはサーバーを設定し、コンテンツハッシュ付きのファイル名にはCache-Control: public, max-age=31536000, immutableを、index.htmlにはno-cacheを設定してください。

アクセシビリティの選択ルール

  • インタラクティブなコンポーネントを構築する際は、ARIA属性とキーボード操作のハンドラー(適宜Enter、Space、Escape)を追加し、タスクの完了とする前にaxe-coreでテストしてください。
  • フォームを構築する際は、すべての<input>をhtmlFor/idで<label>に関連付け、aria-describedbyで紐付けた視認可能なエラーメッセージを表示し、キーボードのみの操作でフォーム全体を使用できるようにしてください。
  • 色で意味(例:エラー状態、ステータスバッジ)を伝える場合は、色覚に特性のあるユーザーが状態を区別できるよう、必ず二次的な表示(アイコン、テキスト、パターン)も併用してください。
  • モーダルやドロップダウンを追加する際は、開いている間は要素内にフォーカスを閉じ込め、閉じたときはトリガー要素にフォーカスを戻してください。マウスを使わずに全体の操作フローをTabキーでたどってテストしてください。
  • プルリクエストで新しいインタラクティブなコンポーネントを追加する場合は、マージ前にWCAG 2.1 AA違反がゼロであることを検証するaxe-coreの統合テストを含める必要があります。

CSS デバッグの判断ルール

レイアウトが崩れた場合は、症状に基づいて診断します。

  • Flex の子要素がコンテナーからはみ出す:Flex の子要素に min-width: 0(行方向)または min-height: 0(列方向)を追加します。Flex アイテムのデフォルトは min-width: auto で、コンテンツサイズより小さく縮むのを防ぎます。
  • Grid アイテムが列幅を無視する:1fr の代わりに minmax(0, 1fr) を使います。単独の 1fr は minmax(auto, 1fr) を意味するため、コンテンツによって列が意図した幅より広くなることがあります。
  • position: sticky で固定されない:すべての祖先要素で overflow: hidden、overflow: auto、または overflow: scroll が指定されていないか確認します。これらはどれも新しいスクロールコンテキストを作り、その中に sticky 要素を閉じ込めます。また、要素に top/bottom の値が設定されていることも確認します。
  • margin: auto で要素が中央揃えにならない:要素に明示的な width が設定されている(ブロック要素の場合)か、親要素に display: flex が設定されている(Flex の子要素の場合)ことを確認します。全幅のブロック要素では margin: auto は機能しません。
  • z-index が機能しない:スタッキングコンテキストを作るには、要素に position: relative/absolute/fixed/sticky が必要です。また、祖先要素が(transform、opacity < 1、filter、will-change によって)スタッキングコンテキストを作り、z-index の適用範囲を制限していないか確認します。
  • inline-block/inline 要素間に隙間ができる:HTML 内の空白によって隙間ができます。対処法:親要素に display: flex を設定する(推奨)か、親要素に font-size: 0 を設定して、子要素で元に戻します。
  • 隣接する要素間に 1px の隙間ができる:サブピクセルレンダリングが原因です。対処法:デバッグ時は border の代わりに outline を使うか、親要素に background を設定して隙間を隠します。
  • 省略記号によるテキストの切り詰めが機能しない:overflow: hidden、white-space: nowrap、text-overflow: ellipsis の3つすべてが必要です。複数行のテキストを切り詰める場合は、display: -webkit-box と -webkit-box-orient: vertical を設定して -webkit-line-clamp を使います。
  • コンテナークエリが適用されない:祖先要素に container-type: inline-size(または size)が設定されていることを確認します。要素自体を自身のコンテナーにはできません。クエリは親要素を参照する必要があります。

ビルドツールの判断ルール

  • Vite(デフォルト):新規プロジェクトではすべてこれを使います。ネイティブ ESM による即時起動の開発サーバー、高速な HMR、Rollup ベースの本番ビルドを利用できます。Vite で動作しない CommonJS 専用の依存関係をプロジェクトが使用している場合は、それらのパッケージを optimizeDeps.include に追加します。Webpack に切り替えてはいけません。
  • Webpack:すでに使用している既存プロジェクト、または Vite に相当するものがない特定の Webpack 専用プラグインが必要な場合(まれ)のみに使います。機会があれば Vite に移行します(「移行の判断ルール」を参照)。
  • Turbopack:Next.js 14+ 内(next dev --turbo)でのみ使います。単体ではまだ使用できません。Next.js プロジェクトでは、Webpack より高速な開発ビルドを提供します。ビルドがすでに高速(5秒未満)なら、切り替える必要はありません。
  • esbuild:ライブラリのバンドル、スクリプト、CLI ツールに使います。フルアプリケーションには使いません。HTML エントリーポイントの処理や、ルートごとのコード分割には対応していません。npm パッケージを公開する場合は tsup(esbuild ラッパー)を使います。
  • SWC と Babel の比較:SWC(Vite プラグインまたは Next.js のデフォルト経由)を使います。SWC は変換処理が20~70倍高速です。Babel 専用で SWC に相当するものがないプラグインにプロジェクトが依存している場合に限り Babel を使います(そのようなケースはますますまれになっています)。
  • モノレポツール:パッケージが5個未満なら、npm/pnpm workspaces を使い、追加ツールは不要です。5~20個なら、タスクのキャッシュに Turborepo を使います。20個を超える場合、または複数言語を使う場合は Nx を使います。単一パッケージのプロジェクトにモノレポツールを追加してはいけません。

SSR とサーバーコンポーネントの判断ルール

SSR、CSR、SSG を使い分ける基準
  • SSG(静的サイト生成):1時間に1回未満しか更新されないコンテンツ(マーケティングページ、ドキュメント、ブログ記事)に使用します。デプロイ時にビルドします。TTFB を可能な限り短縮できます。
  • SSR(サーバーサイドレンダリング):パーソナライズされたコンテンツ(ダッシュボード、ユーザープロフィール)、動的データを含む SEO が重要なページ、または古いデータが許容されないページに使用します。リクエストのたびにサーバーのレイテンシーが加わるため、Cache-Control または CDN を使って積極的にキャッシュします。
  • CSR(クライアントサイドレンダリング):SEO が重要ではなく、インタラクティブ性を優先する、認証済みユーザー専用のページ(管理パネル、内部ツール)に使用します。構築とデプロイが最も簡単です(静的ホスティング)。
  • ISR(インクリメンタル静的再生成):ほとんど静的でありながら、定期的な更新が必要なページ(商品一覧、価格ページ)に使用します。データの古さがどの程度まで許容されるかに応じて、revalidate の間隔を設定します。価格なら60秒、ブログ記事なら3600秒です。
React Server Components(RSC)の判断基準
  • Server Components をデフォルトにする:インタラクティブ性が必要でない限り、すべてのコンポーネントは Server Component です。コンポーネントに onClick、useState、useEffect、またはブラウザー API がある場合は、Client Component('use client')にする必要があります。
  • データ取得:Server Components では async/await を直接使ってデータを取得します。useEffect も TanStack Query もローディング状態も不要です。HTML が送信される前にデータの解決が完了します。
  • Server Components での認証:Server Component でセッション/Cookie を読み取り、ユーザーオブジェクトを prop として Client Components に渡します。Client Components で Cookie やセッションを読み取らないでください。これらはクライアント上で実行されます。
  • Server Actions と API ルートの使い分け:UI から呼び出されるフォーム送信やミューテーションには Server Actions('use server')を使用します。Webhook、サードパーティとの連携、または外部サービスから呼び出されるエンドポイントには API ルート(/api/*)を使用します。
  • Server Components と Client Components 間での状態共有:シリアライズ可能なデータを Server から Client へ prop として渡します。サーバーデータをもとにクライアント側の状態を作る場合は、サーバーから渡された prop で useState を初期化し、クライアント側で更新を管理します。
  • 大規模なデータセット:<Suspense> 境界を使ってストリーミングします。処理の遅い Server Component を <Suspense fallback={<Skeleton />}> で囲みます。シェルはすぐに描画され、処理の遅いコンポーネントは準備ができ次第ストリーミングされます。Suspense 境界は、コンポーネントごとではなく、意味のある UI セクション(サイドバー、メインコンテンツ、コメント)に配置します。
ハイドレーションエラーの判断フロー
  • エラー:「Text content does not match」:サーバーとクライアントのレンダリングで値が異なっています。よくある原因:
    1. レンダリング中の Date.now()、Math.random()、または new Date().toLocaleString() → useEffect と useState に移します。
    2. typeof window !== 'undefined' による条件付きレンダリング → useEffect を使って hasMounted フラグを設定します。
    3. ブラウザー拡張機能による DOM ノードの挿入 → 自分の不具合ではありませんが、シークレットモードでテストして確認します。
  • エラー:「Hydration failed because the server rendered HTML didn't match」:構造上の差異(要素の違い)があります。よくある原因:
    1. <p> の中に <p>、または <p> の中に <div> をネストしている → ネストを修正します。HTML 仕様ではこれらは許可されていません。
    2. サードパーティ製コンポーネントがサーバーとクライアントで異なるレンダリングを行う → dynamic(() => import(...), { ssr: false }) でラップします。
    3. 終了タグがない、または HTML で明示的な終了タグが必要な箇所で自己終了タグを使っている。

パフォーマンスのデバッグワークフロー

ページの読み込みが遅い場合は、この順序で進めてください。手順を飛ばさないでください。

ステップ1:ボトルネックの種類を特定する
  • Lighthouse を実行します。どの指標に問題があるか確認します。
    • LCP >2.5s:最も大きな表示要素の読み込みが遅すぎます。ステップ2に進みます。
    • FID/INP >200ms:JavaScript によってユーザーの操作がブロックされています。ステップ3に進みます。
    • CLS >0.1:初回描画後にレイアウトがずれています。ステップ4に進みます。
ステップ 2:LCP を改善する
  1. LCP 要素を確認します(Lighthouse に表示されます)。通常は、ヒーロー画像、見出しテキスト、または動画です。
  2. 画像の場合:priority(Next.js)または fetchpriority="high" を追加します。明示的に width/height を指定します。WebP/AVIF 形式で配信します。CDN から配信している場合は、キャッシュヘッダーを確認します。
  3. テキストの場合:レンダリングをブロックするフォントがないか確認します。font-display: swap または font-display: optional を使用します。<link rel="preload" as="font" crossorigin> で重要なフォントファイルをプリロードします。
  4. JS によってブロックされている場合:バンドルが大きすぎます。ステップ 3 を確認します。LCP 要素を生成する JS が読み込まれて実行されるまで、LCP 要素はレンダリングできません。
  5. サーバーの応答時間(TTFB)を確認します。TTFB が 600ms を超える場合、ボトルネックはフロントエンドではなく、バックエンドまたは CDN にあります。
ステップ 3:INP(次の描画までのインタラクション)を改善する
  1. Chrome DevTools → Performance を開き、クリック/入力操作を記録します。
  2. 長いタスク(50ms 超の黄色いバー)を探します。クリックすると、コールスタックを確認できます。
  3. 長いタスクが自分のコードの場合:requestIdleCallback、scheduler.yield()(Chrome 115 以降)、または setTimeout(fn, 0) を使って作業を小さな単位に分割し、フレーム間でブラウザーに処理を譲ります。
  4. 長いタスクがReact の再レンダリングの場合:React DevTools Profiler を開き、再レンダリングされるコンポーネントを探します。一般的な対処法は、(コストの高い子コンポーネントに対する)React.memo、(派生値に対する)useMemo、または state を使用箇所の近くに移動することです。1 回の state 変更で 500 個を超えるコンポーネントが再レンダリングされる場合、state の位置がツリー内で高すぎます。
  5. 長いタスクがサードパーティのスクリプト(分析、広告)の場合:async または defer 属性で遅延させるか、requestIdleCallback の後に読み込みます。
ステップ 4:CLS を改善する
  1. どの要素がずれるか確認します(Lighthouse に表示されます。または DevTools →「Layout Shift Regions」を使います)。
  2. 寸法が指定されていない画像/動画:width および height 属性(または aspect-ratio CSS)を追加します。ブラウザーはアセットの読み込み前に領域を確保します。
  3. フォントの切り替え:フォールバックフォントのメトリクスが Web フォントと異なります。@font-face の size-adjust を使ってメトリクスを合わせるか、font-display: optional を使って切り替え自体をなくします。
  4. ファーストビューより上に動的コンテンツが挿入される場合:広告、バナー、Cookie 通知などです。コンテナーに min-height を設定して領域を確保します。コンテンツの高さが変動する場合は、contain: layout を使ってずれの伝播を防ぎます。
  5. 遅れて読み込まれる CSS:初回描画後にスタイルシートが読み込まれて表示レイアウトが変わる場合は、重要な CSS をインライン化するか、スタイルシートをプリロードします。

コード品質の判断ルール

  • テストを作成する際は、すべてのコンポーネントに、主要なレンダリングパスを対象とするユニットテストを少なくとも 1 つ、インタラクション(クリック、キーボード操作)テストを 1 つ設けます。CI のカバレッジゲートで、行カバレッジ 80%以上を必須にします。
  • プロジェクトを開始する際は、初日から tsconfig.json で strict: true を有効にします。コードベースの成長に伴い、後から厳格モードに移行する難易度は指数関数的に高くなります。
  • API 呼び出しまたは非同期処理が失敗した場合は、ユーザー向けのエラーメッセージと再試行アクションを表示します。エラーを黙って握りつぶしたり、生の例外テキストを表示したりしてはいけません。
  • コンポーネントファイルが 300 行を超えた場合は、共有バレルエクスポートを備えた小さなサブコンポーネントに分割します。大きなファイルは、責務が混在していることを示します。
  • CI を設定する際は、lint(ESLint)、型チェック(tsc --noEmit)、テスト(Vitest/Jest)、バンドルサイズチェックを必須ゲートに含めます。マージ前に 4 項目すべてを通過する必要があります。

ワークフロー

ステップ 1:プロジェクトのセットアップとアーキテクチャ
  • 適切なツールを備えた最新の開発環境をセットアップします。
  • ビルドの最適化とパフォーマンス監視を設定します。
  • テストフレームワークと CI/CD の統合を確立します。
  • コンポーネントアーキテクチャとデザインシステムの基盤を作成します。
ステップ 2:コンポーネントの開発
  • 適切な TypeScript 型を備えた、再利用可能なコンポーネントライブラリを作成します。
  • モバイルファーストのアプローチでレスポンシブデザインを実装します。
  • コンポーネントに最初からアクセシビリティを組み込みます。
  • すべてのコンポーネントに対して、包括的なユニットテストを作成します。
ステップ 3:パフォーマンスの最適化
  • コード分割と遅延読み込みの戦略を実装します。
  • 画像とアセットをウェブ配信用に最適化します。
  • Core Web Vitals を監視し、それに応じて最適化します。
  • パフォーマンス予算と監視を設定します。
ステップ 4:テストと品質保証
  • 包括的なユニットテストと統合テストを作成します。
  • 実際の支援技術を使用してアクセシビリティテストを実施します。
  • クロスブラウザー互換性とレスポンシブ動作をテストします。
  • 重要なユーザーフローに対してエンドツーエンドテストを実装します。

リファレンス

Lighthouse CI の目標
  • パフォーマンススコア:90 以上
  • すべてのインタラクティブ要素をキーボードで操作でき、フォーカスインジケーターが表示されること
  • axe-core:WCAG 2.1 AA レベルで違反がゼロであること
  • バンドルサイズ:初期 JS は 200 KB 未満(gzip 圧縮後。bundlesize または同等のツールで強制)
  • 本番コードで TypeScript の any キャストをゼロにし、tsconfig で strict: true を有効にすること

移行の判断基準

  • CRA から Vite へ:CRA のビルド時間が 30 秒を超える場合、または eject を求める声が強まった場合に移行します。手順:react-scripts を vite と @vitejs/plugin-react に置き換え、index.html をルートに移動し、process.env を import.meta.env に更新して、CommonJS のインポートを修正します。所要時間:小規模アプリでは 2~4 時間、カスタム Webpack 設定を使用する大規模アプリでは 1~2 日です。
  • Pages Router から App Router(Next.js)へ:段階的に移行します。一度に 1 つのルートを app/ ディレクトリに移動します。静的またはシンプルなページから始め、複雑なページ(getServerSideProps と複雑な状態を含むもの)は最後にします。アプリ全体を一度に移行しないでください。所要時間:複雑なアプリでは 1 日あたり 1 ルートです。
  • JavaScript から TypeScript へ:tsconfig で allowJs: true を有効にし、ファイル名を .js から .tsx に 1 つずつ変更します。依存関係のないリーフコンポーネントから始め、内側へ進めます。新しいファイルでは初日から strict: true を適用し、必要に応じてベース設定を拡張する tsconfig.strict.json を作成します。所要時間:1 日あたり約 10 ファイルです。
  • CSS Modules から Tailwind へ:すべてのスタイルを一度に書き換えないでください。CSS Modules と並行して Tailwind をインストールします。新しいコンポーネントでは Tailwind を使用し、既存のコンポーネントは別の理由で変更する際にのみ移行します。所要時間:通常の開発中にコンポーネントごとに移行します。
  • クラスコンポーネントからフックへ:機能変更のためにコンポーネントを変更する必要がある場合にのみ移行します。安定していて変更のないクラスコンポーネントは移行しないでください。そのまま問題なく動作します。移行する場合:componentDidMount を useEffect(..., []) に、componentDidUpdate を依存配列付きの useEffect に、this.state を useState に置き換えます。

状態管理の判断マトリクス

  • 状態を共有するコンポーネントが 5 個未満:props で状態を親に持ち上げます。ライブラリは不要です。props のバケツリレーが 3 階層を超える場合は、その特定の状態領域に React Context を追加します。
  • 状態を共有するコンポーネントが 5~15 個:構造化された状態には React Context と useReducer を、よりシンプルな API には Zustand を使用します。状態がサーバーデータの場合は Context ではなく TanStack Query を使用します。
  • コンポーネントが 15 個を超える、または派生状態が複雑:Zustand(シンプルでバンドルが小さい)または Jotai(アトミックでボトムアップ)を使用します。Redux Toolkit は、チームがすでに Redux に精通している場合にのみ使用します。新しいプロジェクトに Redux を導入しないでください。
  • サーバー状態(API データ、キャッシュ、同期):TanStack Query(React)、SWR(よりシンプルな要件)、または Apollo Client(GraphQL)を使用します。サーバーデータをクライアント状態管理ツール(Zustand、Redux)に保存しないでください。古いデータやキャッシュ管理の重複につながります。
  • フォーム状態:複雑なフォーム(フィールドが 5 個を超えるもの、バリデーション、動的フィールド)には react-hook-form を使用します。シンプルなフォーム(フィールドが 5 個未満で、複雑なバリデーションがないもの)にはネイティブの useState を使用します。フォーム状態にグローバル状態管理ツールを使用しないでください。
  • URL 状態(フィルター、ページネーション、検索):URL の検索パラメーター(useSearchParams)を信頼できる唯一の情報源として使用します。派生値についてのみコンポーネント状態と同期します。これにより、状態を共有、ブックマークでき、戻るボタンにも適切に対応できます。

自己検証プロトコル

フロントエンドの実装を完了したら、以下を検証します。

  • Lighthouse CI を実行し、Performance >=90、Accessibility >=90、Best Practices >=90 であることを確認します。いずれかのスコアが90未満の場合は、マージ前に修正します。
  • 新しいページ/コンポーネントごとに npx axe-core または axe-playwright を実行します。WCAG 2.1 AA の違反がゼロであることを確認します。
  • キーボード操作をテストします。ページ全体を Tab キーで移動します。すべてのインタラクティブ要素に到達して操作できる必要があります。フォーカスインジケーターが見えることを確認します。
  • ブラウザーの DevTools で Performance タブを開き、ユーザー操作を記録して、レイアウトスラッシング(強制リフロー)、50ms を超える長時間タスク、過剰な再レンダリングがないか確認します。
  • バンドルサイズを確認します。npx bundlesize を実行するか、ビルド出力を確認します。初期 JS が gzip 圧縮後に200KBを超える場合は、バンドルに何が含まれているか調査します(npx source-map-explorer を使用します)。
  • 320px、768px、1280px でレスポンシブレイアウトをテストします。横スクロール、要素の重なり、読みにくいテキストがないことを確認します。
  • ブラウザーの開発者ツールでネットワークスロットリングを「Slow 3G」に設定してテストします。ページが使えない場合は、読み込み状態を追加し、クリティカルレンダリングパスを最適化します。

障害からの復旧

  • コンポーネントが過剰に再レンダリングされる:React DevTools Profiler を使って原因を特定します。よくある対処法は、(コストのかかる子コンポーネントに)React.memo を使う、(派生値や props として渡すコールバックに)useMemo/useCallback を使う、または状態をその使用箇所に近づけることです。
  • バンドルサイズが急増した:source-map-explorer を実行して原因を特定します。よくある原因は、ライブラリ全体のインポート(import _ from 'lodash' → import groupBy from 'lodash/groupBy')、開発用依存関係の誤ったバンドル、またはサイズの大きなポリフィルです。
  • ハイドレーションの不一致(SSR/SSG):サーバーでレンダリングされた HTML とクライアント側のレンダリング結果が異なっています。よくある原因は、レンダリング内で Date.now() または Math.random() を使うこと、SSR 中に window/document にアクセスすること、クライアント側だけの状態に基づく条件付きレンダリングです。対処法:クライアント側だけで使う値には useEffect を使うか、最終手段として suppressHydrationWarning を使います。
  • 特定のビューポートで CSS レイアウトが崩れる:固定ピクセル幅、flex の子要素に min-width: 0 が指定されていないこと、コンテンツを切り取る overflow: hidden、またはコンテナーからはみ出す position: absolute の要素がないか確認します。固定幅の代わりに、minmax() を使った CSS Grid を使用します。
  • API 呼び出しが多すぎる:useEffect の依存配列が欠けている、または正しくない可能性を確認します。TanStack Query を使っている場合は、マウントのたびに再取得しないよう staleTime を設定します。データ取得に useEffect を使っている場合は、データ取得ライブラリへの切り替えを検討します。

既存のコードベースの把握

既存のフロントエンドプロジェクトに参加する際は、以下を実施します。

  1. npm start/npm run dev を実行する(5分)— 失敗した場合は、まず開発環境を修正します。
  2. 技術スタックを確認する(5分)— フレームワーク(React/Vue/Angular/Svelte)、バンドラー(Vite/Webpack/Turbopack)、CSS の方式(Tailwind/CSS Modules/styled-components)、状態管理、テストライブラリを確認します。
  3. テストスイートを実行する(5分)— npm test を実行します。カバレッジ、テストの種類、テストされていないフローを確認します。
  4. ブラウザーの DevTools を開く(10分)— バンドルサイズ(Network タブ)、コンソールエラー、Lighthouse のスコアを確認します。これらが基準値になります。
  5. コンポーネントツリーを読む(10分)— アプリのルートから始めます。トップレベルのルートと主要なコンポーネントを把握します。共有コンポーネント(デザインシステム、レイアウト)を特定します。
  6. 技術的負債の兆候を確認する(5分)— any の型キャスト、eslint-disable コメント、500行を超えるファイル、インラインスタイルがないか確認します。これらは壊れやすい箇所の兆候です。
  7. データフローを特定する(10分)— API から UI まで、データがどのように流れるかを確認します。API クライアントは一元化されていますか? 読み込み/エラー状態はありますか? サーバーの状態はライブラリで管理されていますか、それとも手動で管理されていますか?

スクリプト

  • scripts/check_bundle.sh -- ビルド出力ディレクトリを解析し、JS と CSS のバンドルサイズを gzip 圧縮時の推定値と設定可能な警告/エラーしきい値とともに表示します。オプションを確認するには --help を指定して実行してください。

React + TypeScript で実装された仮想化 DataTable の全体例については、DataTable Component を参照してください。 複合コンポーネント、カスタムフック、エラーバウンダリー、楽観的 UI、フォーム検証のパターンについては、React Component Patterns を参照してください。 判別可能なユニオン、多相コンポーネント、ブランド型、型安全な API クライアントについては、TypeScript Patterns を参照してください。 最新の CSS Grid レイアウト、コンテナクエリ、テーマ設定、ビュー遷移、:has() セレクターについては、CSS Patterns を参照してください。

コメント