この記事で扱うこと
認証が必要なアプリのホームで LCP・転送量・CLS を改善するには、「何を直したか」と同じくらいどう測定したかが重要だ。同じ日に公開の価格ページの SEO(JSON-LD) と装飾 motion のアクセシビリティも手を入れた。
一日のまとめ
| 作業 | 何をしたかったか | やったこと | 結果 |
|---|---|---|---|
| 測定 | before/after を信頼できるプロトコルで比較 | reload 3回 median でのみ比較; 単一 outlier run を除外 | 開発サーバー trace の分散に揺れない比較 |
| Prefetch + CLS | データは速く来るがレイアウトジャンプなしで | サーバーの prefetch query string をクライアント provider default と一致 + skeleton 高さ予約 | cache hit 維持、skeleton→コンテンツ CLS 抑制 |
| キャラクセル | ビューポート内のスライドだけ画像を mount | in-view スライドだけ画像を mount | 不要な転送量を削減 |
| SEO | JSON-LD をサーバーで安全に注入 | JSON-LD graph をサーバー page.tsx で注入; < を sanitize | クローラー・一貫性を確保 |
| A11y | prefers-reduced-motion で装飾 ripple を無効化 | ripple を prefers-reduced-motion で無効化 | reduced-motion ユーザーに装飾 motion を除去 |
1. Lab 測定プロトコル
背景知識(概念)
- LCP (Largest Contentful Paint): 画面で最も大きなコンテンツが描画されるまでの時間。
- Trace outlier: 開発サーバーの単一 Performance trace は数値がばらつくため、一度の値で結論を出してはいけない異常値。
どんな状況だったか
Chrome Performance trace を一度取ると数値がばらついた。開発サーバーの単一 run は outlier が多い。before/after を信頼できるプロトコルで比較したかった。
主な作業
Chrome Performance trace を reload 3回 median でのみ before/after 比較する。単一 outlier run は結論から除外する。
教訓
- Lighthouse の SEO/A11y スコアと trace の LCP ms は別の計測だ。
- 次の段階: production
build && startで同じプロトコルで再測定する。
→ Lab 性能測定の分散と比較プロトコル · Lighthouse 測定精度
2. SSR prefetch と CLS
背景知識(概念)
- CLS (Cumulative Layout Shift): ロード中にレイアウトがずれる度合い。skeleton の高さが実際のコンテンツと違うと悪化する。
- Prefetch: サーバーでデータを先に取得し、クライアントキャッシュに入れておくこと。
どんな状況だったか
SSR で list API を prefetch したが、クライアントの query key が1文字でも違えば cache miss → skeleton → コンテンツの遷移が目立ち、CLS がかえって悪化しうる。データは速く来るがレイアウトジャンプなしでが目標だった。
主な作業
サーバーで list API を prefetch・hydrate するとき、query string をクライアント provider default と一致させる。skeleton は page size の行数・body minHeight の共有で CLS を抑制する。cache hit を維持しつつ skeleton→コンテンツ CLS を抑制した。
教訓
- prefetch がデータを速く取得すると skeleton→コンテンツの遷移が目立ち CLS が悪化しうる — prefetch・skeleton skip・高さ予約は一つのセットとして見る。
→ SSR prefetch とクライアントクエリキャッシュの hydrate · CLS とスケルトンレイアウト予約
3. キャラクセル画像
背景知識(概念)
- ビューポート外の画像の遅延: すべてのスライド画像を mount すると転送量が大きくなる。lazy attribute だけでは「DOMに載る画像数」を減らしにくい場合がある。
どんな状況だったか
すべてのスライド画像を mount すると転送量が不必要に大きい。ビューポート内のスライドだけ画像を mount したかった。
主な作業
キャラクセルは in-view スライドだけ画像を mount する。lazy attribute だけでは「DOMに載る画像数」を減らしにくい場合がある。不要な転送量を削減した。
教訓
- キャラクセルはビューポート外のスライド画像を mount しないことが lazy より転送量削減に直接的だ。
4. JSON-LD
背景知識(概念)
- JSON-LD: ページ内容を検索エンジンが理解できるよう
<script>に入れる構造化データ(JSON)フォーマット。SEO に使う。
どんな状況だったか
公開の pricing ページに構造化データが必要で、クライアントだけで入れるとクローラー・一貫性の面で不利だ。JSON-LD をサーバーで安全に注入したかった。
主な作業
公開の pricing ページに JSON-LD graph をサーバー page.tsx で注入する。< を sanitize する。クローラー・一貫性を確保した。
教訓
- JSON-LD は client UI と分離し、サーバー
page.tsxで sanitize した script として注入する。
5. 装飾 motion のアクセシビリティ
背景知識(概念)
prefers-reduced-motion: ユーザーがモーション削減を好むかを知らせるメディアクエリ。装飾 motion はここで無効化する。
どんな状況だったか
装飾用の ripple のような motion を prefers-reduced-motion ユーザーにもそのまま見せていた。
主な作業
ripple を prefers-reduced-motion で無効化した。reduced-motion ユーザーに装飾 motion を除去した。
教訓
animate-pulseは spread ripple と目的が違う — 専用の@keyframes+ stagger が適する。