フロントエンド開発ガイド は何をしますか?
概要
このガイドでは、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モジュール)に集中化し、認証ヘッダー、エラー変換、リトライが1か所で処理されるようにします。
パフォーマンスの意思決定ルール
- ページのLargest Contentful PaintがLighthouse CIで2.5秒を超える場合は、ブロッキングバグとして扱います。Chrome DevTools Performanceタブでプロファイリングし、マージする前に最大のボトルネックを修正します。
- アニメーションを追加するときは、CSSの
transformとopacityプロパティ(コンポジターのみ)を使用します。width、height、top/leftを使用すると、ジャンクの原因となるレイアウト計算がトリガーされるため、避けてください。 - アプリにオフラインサポートが必要な場合は、静的アセットにはキャッシュファースト戦略、APIリクエストにはネットワークファースト戦略を使用してサービスワーカーを登録し、オフラインの場合はキャッシュされたレスポンスにフォールバックします。
- 初期JSバンドルが予算(例:200 KB gzip圧縮)を超える場合は、
React.lazy()または動的import()を使用してルートベースのコード分割を追加し、重要なスクリプトをフォールドより下に延期します。 - 古いブラウザをサポートする場合は、browserslist設定を定義し、ビルドツール(Vite、Webpack)に自動ポリフィルを任せます。browserslistのカバレッジが不十分でない限り、ポリフィルや機能チェックを手動で追加しないでください。
- 画像を追加するときは、WebP/AVIFソースと明示的な
width/height属性を持つ<picture>を使用して、レイアウトシフトを防ぎます。フォールドより下の画像にはloading="lazy"を追加します。 - 初期ページ読み込み時にルートが必要ない場合は、
React.lazy()(またはフレームワークの同等のもの)と<Suspense>フォールバックでラップし、メインバンドルにそのルートのコードが含まれないようにします。 - 静的アセットを提供するときは、コンテンツハッシュ化されたファイル名には
Cache-Control: public, max-age=31536000, immutableを、index.htmlにはno-cacheを設定するようにCDNまたはサーバーを設定します。
アクセシビリティの意思決定ルール
- インタラクティブなコンポーネントを構築するときは、ARIA属性、キーボードハンドラー(状況に応じて
Enter、Space、Escape)を追加し、タスクを完了としてマークする前にaxe-coreでテストします。 - フォームを構築するときは、すべての
<input>をhtmlFor/idを介して<label>に関連付け、aria-describedbyでリンクされた表示可能なエラーメッセージを提供し、フォームがキーボードのみのナビゲーションで完全に操作可能であることを確認します。 - 色を使用して意味を伝える場合(例:エラー状態、ステータスバッジ)、常にセカンダリインジケーター(アイコン、テキスト、パターン)を含めて、色覚異常のユーザーが状態を区別できるようにします。
- モーダルまたはドロップダウンを追加するときは、要素が開いている間はその中にフォーカスを閉じ込め、閉じるときにフォーカスをトリガー要素に戻します。マウスを使わずにタブですべてのフローをテストします。
- プルリクエストが新しいインタラクティブコンポーネントを追加する場合、PRにはWCAG 2.1 AA違反がゼロであることを主張するaxe-core統合テストを含める必要があります。
CSSデバッグの意思決定ルール
レイアウトが壊れた場合は、症状に基づいて診断します。
- フレックス子がコンテナをオーバーフローする:フレックス子に
min-width: 0(行)またはmin-height: 0(列)を追加します。フレックスアイテムのデフォルトはmin-width: autoで、コンテンツサイズより小さく縮小できません。 - グリッドアイテムが列幅を無視する:
1frの代わりにminmax(0, 1fr)を使用します。単純な1frはminmax(auto, 1fr)を意味し、コンテンツが列を意図よりも広く押し広げる可能性があります。 position: stickyが機能しない:すべての祖先にoverflow: hidden、overflow: auto、またはoverflow: scrollがないか確認します。これらのいずれかが、スティッキー要素を含む新しいスクロールコンテキストを作成します。また、要素にtop/bottom値が設定されていることを確認します。margin: autoで中央揃えされた要素が中央揃えにならない:要素に明示的なwidth(ブロック)があるか、親にdisplay: flex(フレックス子)があることを確認します。margin: autoは、全幅のブロック要素では機能しません。z-indexが機能しない:要素は、重ね合わせコンテキストを作成するためにposition: relative/absolute/fixed/stickyが必要です。また、z-indexのスコープを制限する重ね合わせコンテキスト(transform、opacity < 1、filter、またはwill-changeを介して)を作成する祖先がないか確認します。- インラインブロック/インライン要素間のギャップ:HTMLの空白がギャップを作成します。修正:親に
display: flexを使用するか(推奨)、親にfont-size: 0を設定し、子でリセットします。 - 隣接する要素間の1pxのギャップ:サブピクセルレンダリング。修正:デバッグには
borderの代わりにoutlineを使用するか、親にbackgroundを設定してギャップをマスクします。 - テキストの省略記号による切り詰めが機能しない:
overflow: hidden、white-space: nowrap、text-overflow: ellipsisの3つすべてが必要です。複数行の切り詰めには、-webkit-line-clampをdisplay: -webkit-boxおよび-webkit-box-orient: verticalとともに使用します。 - コンテナクエリが適用されない:祖先に
container-type: inline-size(またはsize)があることを確認します。要素自体を自身のコンテナにすることはできません。クエリは親を参照する必要があります。
ビルドツールの意思決定ルール
- Vite(デフォルト):すべての新しいプロジェクトに使用します。ネイティブESMによるインスタントな開発サーバー、高速なHMR、Rollupベースのプロダクションビルド。プロジェクトがViteで失敗するCommonJSのみの依存関係を使用している場合は、それらのパッケージに
optimizeDeps.includeを追加します。Webpackに切り替えないでください。 - Webpack:既に使用している既存のプロジェクト、または特定のWebpackのみのプラグインにViteの同等のものがない場合(まれ)にのみ使用します。機会があればViteに移行します(移行の意思決定ルールを参照)。
- Turbopack:Next.js 14+(
next dev --turbo)内でのみ使用します。スタンドアロンではまだ使用できません。Next.jsプロジェクトのWebpackよりも高速な開発ビルドを提供します。ビルドがすでに高速(<5秒)の場合は、切り替える必要はありません。 - esbuild:ライブラリのバンドル、スクリプト、またはCLIツールに使用します。完全なアプリケーションには使用しないでください。HTMLエントリポイントの処理やルートごとのコード分割はありません。npmパッケージを公開するには
tsup(esbuildラッパー)を使用します。 - SWC vs Babel:SWC(ViteプラグインまたはNext.jsデフォルトを介して)を使用します。SWCは変換で20〜70倍高速です。Babelは、プロジェクトがSWCに相当するもののないBabelのみのプラグインに依存している場合にのみ使用します(ますますまれです)。
- モノレポツール:<5パッケージ = npm/pnpmワークスペース、追加ツールなし。5-20パッケージ = タスクキャッシング用のTurborepo。>20パッケージまたはポリグロット = Nx。シングルパッケージのプロジェクトにモノレポツールを追加しないでください。
SSRとサーバーコンポーネントの意思決定ルール
SSR vs CSR vs SSGをいつ使用するか
- SSG(静的サイト生成):1時間に1回未満しか変更されないコンテンツ(マーケティングページ、ドキュメント、ブログ記事)に使用します。デプロイ時にビルドします。可能な限り高速なTTFB。
- SSR(サーバーサイドレンダリング):パーソナライズされたコンテンツ(ダッシュボード、ユーザープロファイル)、動的データを含むSEOに重要なページ、または古いデータが許容できないページに使用します。リクエストごとにサーバーレイテンシが追加されます。
Cache-ControlまたはCDNで積極的にキャッシュします。 - CSR(クライアントサイドレンダリング):SEOが重要ではなく、インタラクティビティが優先される認証済みのみのページ(管理パネル、内部ツール)に使用します。構築とデプロイが最も簡単です(静的ホスティング)。
- ISR(インクリメンタル静的再生成):ほとんど静的であるが定期的な更新が必要なページ(製品リスト、価格ページ)に使用します。データがどの程度古くなってもよいかに基づいて
revalidate間隔を設定します。価格は60秒、ブログ記事は3600秒。
React Server Components(RSC)の意思決定ルール
- デフォルトでサーバーコンポーネント:インタラクティブ性が必要でない限り、すべてのコンポーネントはサーバーコンポーネントです。コンポーネントに
onClick、useState、useEffect、またはブラウザAPIがある場合は、クライアントコンポーネント('use client')である必要があります。 - データフェッチ:サーバーコンポーネントで
async/awaitを直接使用してデータをフェッチします。useEffect、TanStack Query、ローディング状態は必要ありません。データはHTMLが送信される前に解決されます。 - サーバーコンポーネントでの認証:サーバーコンポーネントでセッション/クッキーを読み取り、ユーザーオブジェクトをプロップとしてクライアントコンポーネントに渡します。クライアントコンポーネントでクッキーやセッションを読み取らないでください。これらはクライアントで実行されます。
- サーバーアクションとAPIルートの使い分け:UIから呼び出されるフォーム送信やミューテーションにはサーバーアクション(
'use server')を使用します。Webhook、サードパーティの統合、または外部サービスによって呼び出されるエンドポイントにはAPIルート(/api/*)を使用します。 - サーバーコンポーネントとクライアントコンポーネント間の状態共有:シリアライズ可能なデータをサーバーからクライアントにプロップとして渡します。サーバーデータから派生したクライアント側の状態が必要な場合は、サーバープロップで
useStateを初期化し、クライアントで更新を管理します。 - 大規模なデータセット:
<Suspense>境界でストリーミングします。遅いサーバーコンポーネントを<Suspense fallback={<Skeleton />}>でラップします。シェルはすぐにレンダリングされ、遅いコンポーネントは準備ができたときにストリーミングされます。Suspense境界は、意味のあるUIセクション(サイドバー、メインコンテンツ、コメント)に配置します。すべてのコンポーネントに配置しないでください。
ハイドレーションエラーの判断ツリー
- エラー:「テキストコンテンツが一致しません」:サーバーとクライアントのレンダリングで値が異なります。一般的な原因:
- レンダリングでの
Date.now()、Math.random()、またはnew Date().toLocaleString()→useEffect+useStateに移動します。 typeof window !== 'undefined'条件付きレンダリング →useEffectを使用してhasMountedフラグを設定します。- ブラウザ拡張機能がDOMノードを挿入 → あなたのバグではありませんが、シークレットモードでテストして確認します。
- レンダリングでの
- エラー:「サーバーがレンダリングしたHTMLが一致しなかったため、ハイドレーションに失敗しました」:構造の違い(異なる要素)。一般的な原因:
<p>が<p>内にネストされている、または<div>が<p>内にある → ネストを修正します。HTML仕様では許可されていません。- サードパーティのコンポーネントがサーバーとクライアントで異なるレンダリングを行う →
dynamic(() => import(...), { ssr: false })でラップします。 - 閉じタグがない、またはHTMLが明示的な閉じを必要とする自己終了タグ。
パフォーマンスデバッグのワークフロー
ページが遅い場合は、次の順序に従ってください。手順をスキップしないでください。
ステップ1:ボトルネックの種類を特定する
- Lighthouseを実行します。どのメトリックが失敗しているかを確認します。
- LCP >2.5秒:最も大きな表示要素の読み込みが遅すぎます。ステップ2に進みます。
- FID/INP >200ms:ユーザーインタラクションがJavaScriptによってブロックされています。ステップ3に進みます。
- CLS >0.1:最初のペイント後にレイアウトシフトが発生します。ステップ4に進みます。
ステップ2:LCPを修正する
- LCP要素が何であるかを確認します(Lighthouseが表示します)。通常:ヒーロー画像、見出しテキスト、または動画。
- 画像の場合:
priority(Next.js)またはfetchpriority="high"を追加します。明示的なwidth/heightを追加します。WebP/AVIFで配信します。CDNから配信される場合は、キャッシュヘッダーを確認します。 - テキストの場合:レンダリングブロックフォントがないか確認します。
font-display: swapまたはfont-display: optionalを使用します。重要なフォントファイルを<link rel="preload" as="font" crossorigin>でプリロードします。 - JSによってブロックされている場合:バンドルが大きすぎます。ステップ3を確認します。LCP要素は、それを作成するJSが読み込まれて実行されるまでレンダリングできません。
- サーバーの応答時間(TTFB)を確認します。TTFB >600msの場合、ボトルネックはバックエンドまたはCDNであり、フロントエンドではありません。
ステップ3:INP(Interaction to Next Paint)を修正する
- Chrome DevTools → Performance → クリック/タイプのインタラクションを記録します。
- ロングタスク(>50msの黄色いバー)を見つけます。クリックして呼び出しスタックを表示します。
- ロングタスクが自分のコードの場合:
requestIdleCallback、scheduler.yield()(Chrome 115+)、またはsetTimeout(fn, 0)を使用して作業を小さなチャンクに分割し、フレーム間でブラウザに処理を譲ります。 - ロングタスクがReactの再レンダリングの場合:React DevTools Profilerを開きます。再レンダリングするコンポーネントを見つけます。一般的な修正:
React.memo(高価な子)、useMemo(派生値)、または状態を使用場所に近づけます。単一の状態変更で500を超えるコンポーネントが再レンダリングされる場合、状態がツリー内で高すぎます。 - ロングタスクがサードパーティのスクリプト(分析、広告)の場合:
asyncまたはdefer属性で延期するか、requestIdleCallbackの後に読み込みます。
ステップ4:CLSを修正する
- どの要素がシフトするかを確認します(Lighthouseが表示するか、DevTools → "Layout Shift Regions"を使用します)。
- 寸法のない画像/動画:
widthとheight属性(またはaspect-ratioCSS)を追加します。ブラウザはアセットが読み込まれる前にスペースを予約します。 - フォントスワップ:フォールバックフォントはWebフォントとは異なるメトリックを持っています。
@font-faceでsize-adjustを使用してメトリックを一致させるか、font-display: optionalを使用してスワップを完全に排除します。 - フォールドより上に動的コンテンツが挿入される:広告、バナー、クッキー通知。
min-heightでコンテナにスペースを予約します。コンテンツの高さが異なる場合は、contain: layoutを使用してシフトが伝播するのを防ぎます。 - 遅延読み込みCSS:最初のペイント後にスタイルシートが読み込まれ、表示レイアウトが変更される場合は、クリティカルCSSをインライン化するか、スタイルシートをプリロードします。
コード品質の意思決定ルール
- テストを作成するときは、すべてのコンポーネントに、主要なレンダリングパスをカバーする少なくとも1つのユニットテストと1つのインタラクションテスト(クリック、キーボード)が必要です。CIカバレッジゲートで最低80%の行カバレッジを強制します。
- プロジェクトを開始するときは、初日から
tsconfig.jsonでstrict: trueを有効にします。後でstrictモードをレトロフィットすると、コードベースが大きくなるにつれて指数関数的に難しくなります。 - API呼び出しまたは非同期操作が失敗した場合は、再試行アクションを含むユーザー向けのエラーメッセージを表示します。エラーを暗黙的に飲み込んだり、生の例外テキストを表示したりしないでください。