ストーリー
高橋アーキテクトが報告書を見せた。
ミッション概要
| ミッション | テーマ | 難易度 |
|---|---|---|
| Mission 1 | Lighthouse結果の分析 | 初級 |
| Mission 2 | 画像最適化の実装 | 初級 |
| Mission 3 | バンドルサイズの削減 | 中級 |
| Mission 4 | CLS修正 | 中級 |
| Mission 5 | CDN設計 | 中級 |
| Mission 6 | 改善計画書の作成 | 上級 |
Mission 1: Lighthouse結果の分析(10分)
以下のLighthouse結果から問題点を洗い出し、改善の優先順位を付けてください。
Performance Score: 45/100
Metrics:
FCP (First Contentful Paint): 3.8s
LCP (Largest Contentful Paint): 6.2s
TBT (Total Blocking Time): 890ms
CLS (Cumulative Layout Shift): 0.35
Speed Index: 5.1s
Opportunities:
- Eliminate render-blocking resources: Save 1.5s
- Properly size images: Save 2.8s
- Remove unused JavaScript: Save 1.2s
- Serve images in next-gen formats: Save 0.8s
- Enable text compression: Save 0.5s
Diagnostics:
- Image elements do not have explicit width and height
- Avoid enormous network payloads (Total: 5.2MB)
- JavaScript execution time: 2.8s
- Largest Contentful Paint element: <img src="hero-banner.jpg"> (1.8MB)
解答例
| 優先度 | 問題 | 改善内容 | 期待効果 |
|---|---|---|---|
| 1 | LCPが6.2秒 | Hero画像(1.8MB)の最適化 + preload | LCP 3秒以下に |
| 2 | 画像サイズ未最適化 | WebP変換 + レスポンシブ画像 | 2.8秒改善 |
| 3 | JS実行時間2.8秒 | Code-splitting + Tree-shaking | TBT 200ms以下に |
| 4 | レンダリングブロック | Critical CSS + 非同期JS | FCP 1.5秒改善 |
| 5 | CLS 0.35 | 画像にwidth/height指定 | CLS 0.1以下に |
| 6 | テキスト圧縮未設定 | gzip/brotli有効化 | 0.5秒改善 |
最優先はLCP要因のHero画像。1.8MBは異常に大きく、200KB以下に最適化できる。
Mission 2: 画像最適化の実装(15分)
SpeedShopの商品画像を最適化するHTMLとCSSを書いてください。
要件
- Hero画像: preload + WebP/AVIF対応
- 商品一覧画像: lazy loading + レスポンシブ
- 画像にwidth/heightを明示
解答例
<head>
<!-- Hero画像のpreload(LCPを改善) -->
<link
rel="preload"
as="image"
href="/images/hero-800w.avif"
type="image/avif"
media="(max-width: 800px)"
>
<link
rel="preload"
as="image"
href="/images/hero-1600w.avif"
type="image/avif"
media="(min-width: 801px)"
>
</head>
<body>
<!-- Hero画像: preload済み、遅延読み込みしない -->
<picture>
<source
type="image/avif"
srcset="/images/hero-800w.avif 800w, /images/hero-1600w.avif 1600w"
sizes="100vw"
>
<source
type="image/webp"
srcset="/images/hero-800w.webp 800w, /images/hero-1600w.webp 1600w"
sizes="100vw"
>
<img
src="/images/hero-1600w.jpg"
alt="SpeedShop セールバナー"
width="1600"
height="600"
fetchpriority="high"
>
</picture>
<!-- 商品一覧画像: lazy loading + レスポンシブ -->
<div class="product-grid">
<div class="product-card">
<picture>
<source
type="image/avif"
srcset="/images/product-001-300w.avif 300w,
/images/product-001-600w.avif 600w"
sizes="(max-width: 600px) 300px, 600px"
>
<source
type="image/webp"
srcset="/images/product-001-300w.webp 300w,
/images/product-001-600w.webp 600w"
sizes="(max-width: 600px) 300px, 600px"
>
<img
src="/images/product-001-600w.jpg"
alt="商品名"
width="600"
height="600"
loading="lazy"
decoding="async"
>
</picture>
</div>
</div>
</body>
Mission 3: バンドルサイズの削減(20分)
以下のバンドル分析結果を見て、削減計画を立ててください。
bundle.js: 2.4MB (parsed)
- react: 42KB
- react-dom: 120KB
- moment.js: 290KB (全ロケール含む)
- lodash: 72KB (全関数)
- chart.js: 205KB
- admin-panel: 180KB (管理者のみ使用)
- utils: 15KB
- app-code: 350KB
- vendor-misc: 1,126KB
解答例
// 改善計画
// 1. moment.js → date-fns に置き換え(290KB → 25KB)
// Before
import moment from 'moment';
moment(date).format('YYYY-MM-DD');
// After
import { format } from 'date-fns';
format(date, 'yyyy-MM-dd');
// 2. lodash → 個別インポート(72KB → 5KB使用分のみ)
// Before
import _ from 'lodash';
_.debounce(fn, 300);
// After
import debounce from 'lodash/debounce';
debounce(fn, 300);
// 3. chart.js → lazy loading(205KB → 初期0KB)
// Before
import { Chart } from 'chart.js';
// After
const ChartComponent = lazy(() => import('./components/Chart'));
// 4. admin-panel → Code-splitting(180KB → 初期0KB)
const AdminPanel = lazy(() => import('./pages/AdminPanel'));
// 5. vendor-misc → 精査して不要ライブラリを除去
// 削減結果の見積もり
const results = {
before: '2.4MB',
after: {
initialBundle: '550KB', // react + react-dom + utils + app-code
lazyChunks: '385KB', // chart.js + admin-panel
removed: '362KB', // moment → date-fns, lodash最適化
vendorOptimized: '500KB', // vendor精査
},
improvement: '初期バンドル: 2.4MB → 550KB (77%削減)',
};
Mission 4: CLS修正(15分)
以下のHTMLのCLS問題を特定し、修正してください。
<!-- 問題のあるHTML -->
<div class="page">
<header>SpeedShop</header>
<!-- 問題1: 画像サイズ未指定 -->
<img src="/banner.jpg" alt="バナー">
<!-- 問題2: 動的に高さが変わるコンテンツ -->
<div id="ad-space"></div>
<!-- 問題3: Webフォント切り替え -->
<h1 style="font-family: 'CustomFont', sans-serif;">セール開催中</h1>
<div class="products">...</div>
</div>
解答例
<!-- 修正後のHTML -->
<head>
<!-- フォントのpreload -->
<link rel="preload" href="/fonts/CustomFont.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: 'CustomFont';
src: url('/fonts/CustomFont.woff2') format('woff2');
font-display: swap;
/* size-adjust で代替フォントとのサイズ差を最小化 */
size-adjust: 105%;
}
/* 広告スペースの事前確保 */
.ad-space {
min-height: 250px;
background: #f0f0f0;
}
</style>
</head>
<body>
<div class="page">
<header>SpeedShop</header>
<!-- 修正1: width/heightを明示 + aspect-ratio -->
<img
src="/banner.jpg"
alt="バナー"
width="1200"
height="300"
style="aspect-ratio: 4/1; width: 100%; height: auto;"
>
<!-- 修正2: 広告スペースの高さを事前確保 -->
<div id="ad-space" class="ad-space"></div>
<!-- 修正3: font-display: swap + preload で対応済み -->
<h1 style="font-family: 'CustomFont', sans-serif;">セール開催中</h1>
<div class="products">...</div>
</div>
</body>
修正のポイント:
- 画像: width/height属性でブラウザが事前にスペースを確保
- 動的コンテンツ: min-heightで広告スペースを事前に確保
- フォント: font-display: swap + preload + size-adjust
Mission 5: CDN設計(10分)
SpeedShopのCDN設定(キャッシュルール)を設計してください。
解答例
| パスパターン | TTL | 圧縮 | 備考 |
|---|---|---|---|
/assets/*.{js,css} | 1年 | brotli | ファイル名にハッシュ含む |
/images/* | 24時間 | - | 画像フォーマット自動変換 |
/fonts/* | 1年 | - | immutableヘッダー |
/api/products/* | 1分 | gzip | クエリ文字列をキーに含む |
/api/search* | 30秒 | gzip | クエリ文字列をキーに含む |
/api/cart/* | 0 | gzip | キャッシュしない(Cookie転送) |
/api/orders/* | 0 | gzip | キャッシュしない |
/*.html | 5分 | brotli | must-revalidate |
Mission 6: 改善計画書の作成(20分)
全ミッションの結果をまとめた改善計画書を作成してください。
解答例
=== SpeedShop フロントエンドパフォーマンス改善計画 ===
現状: Lighthouse 45点
目標: Lighthouse 90点以上
Phase 1: 即効性のある改善(1週間)
1. Hero画像最適化(1.8MB → 150KB)→ LCP改善
2. gzip/brotli圧縮の有効化 → 全体転送量削減
3. 画像にwidth/height追加 → CLS改善
期待効果: Lighthouse 65点
Phase 2: バンドル最適化(2週間)
4. moment.js → date-fns 置き換え
5. lodash 個別インポート化
6. AdminPanel のCode-splitting
7. chart.js のLazy Loading
期待効果: Lighthouse 80点
Phase 3: インフラ最適化(1週間)
8. CDN導入と設定
9. Critical CSS のインライン化
10. Webフォントの最適化
期待効果: Lighthouse 90点以上
KPI:
- LCP: 6.2秒 → 2.0秒以下
- TBT: 890ms → 200ms以下
- CLS: 0.35 → 0.1以下
- 初期バンドル: 2.4MB → 500KB以下
達成度チェック
| ミッション | テーマ | 完了 |
|---|---|---|
| Mission 1 | Lighthouse分析 | [ ] |
| Mission 2 | 画像最適化 | [ ] |
| Mission 3 | バンドル削減 | [ ] |
| Mission 4 | CLS修正 | [ ] |
| Mission 5 | CDN設計 | [ ] |
| Mission 6 | 改善計画書 | [ ] |
チェックリスト
- Lighthouse結果を読み解き、優先順位を付けられる
- 画像の最適化(フォーマット、サイズ、読み込み方法)を実装できる
- バンドルサイズの削減計画を立てられる
- CLSの原因を特定して修正できる
- CDNのキャッシュルールを設計できる
次のステップへ
お疲れさまでした。フロントエンドパフォーマンスの実践力が身についたはずです。
次のセクションでは、Step 5の理解度チェックです。
推定所要時間: 90分