ストーリー
高橋アーキテクトがDevToolsのネットワークタブを示した。
画像フォーマットの選択
| フォーマット | 用途 | 圧縮 | 透過 | サイズ比較 |
|---|---|---|---|---|
| JPEG | 写真、複雑な画像 | 非可逆 | なし | 基準 |
| PNG | ロゴ、アイコン | 可逆 | あり | 2-5倍大 |
| WebP | 写真全般 | 両対応 | あり | 25-35%小 |
| AVIF | 写真全般(次世代) | 非可逆 | あり | 40-50%小 |
| SVG | アイコン、図形 | テキスト | あり | 極小 |
レスポンシブ画像
<!-- picture要素で最適なフォーマットを選択 -->
<picture>
<!-- AVIF対応ブラウザにはAVIFを配信 -->
<source
type="image/avif"
srcset="product-400w.avif 400w,
product-800w.avif 800w,
product-1200w.avif 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1200px) 800px,
1200px"
>
<!-- WebP対応ブラウザにはWebPを配信 -->
<source
type="image/webp"
srcset="product-400w.webp 400w,
product-800w.webp 800w,
product-1200w.webp 1200w"
sizes="(max-width: 600px) 400px,
(max-width: 1200px) 800px,
1200px"
>
<!-- フォールバック -->
<img
src="product-800w.jpg"
alt="商品画像"
width="800"
height="600"
loading="lazy"
>
</picture>
画像最適化のテクニック
1. 適切なサイズ
// 画像リサイズのパイプライン
interface ImageResizeConfig {
breakpoints: {
thumbnail: { width: 150, height: 150 };
small: { width: 400 };
medium: { width: 800 };
large: { width: 1200 };
original: { width: null }; // リサイズしない
};
quality: {
webp: 80;
avif: 65;
jpeg: 85;
};
}
// sharp を使った画像処理の例
async function optimizeImage(
inputPath: string,
outputDir: string
): Promise<string[]> {
const sizes = [400, 800, 1200];
const formats = ['webp', 'avif', 'jpeg'];
const outputs: string[] = [];
for (const size of sizes) {
for (const format of formats) {
const output = `${outputDir}/image-${size}w.${format}`;
await sharp(inputPath)
.resize(size)
.toFormat(format, { quality: format === 'avif' ? 65 : 80 })
.toFile(output);
outputs.push(output);
}
}
return outputs;
}
2. 圧縮
// 画像圧縮の効果(例: 1200×800の商品写真)
const compressionResults = {
original: { format: 'JPEG', size: '450KB', quality: 100 },
optimized: { format: 'JPEG', size: '120KB', quality: 85 },
webp: { format: 'WebP', size: '85KB', quality: 80 },
avif: { format: 'AVIF', size: '55KB', quality: 65 },
// 元画像の88%を削減(450KB → 55KB)
};
3. プレースホルダー(LQIP)
// Low Quality Image Placeholder
// ぼやけた極小画像を先に表示し、本画像の読み込みを待つ
const lqipConfig = {
placeholder: {
width: 20, // 20px幅の極小画像
quality: 20, // 低品質
blur: true, // CSSでぼかし
size: '500bytes', // インラインBase64で埋め込み可能
},
};
<!-- LQIP の実装例 -->
<div class="image-container">
<img
src="data:image/webp;base64,UklGR..."
data-src="product-800w.webp"
alt="商品画像"
class="lazy-image blur"
width="800"
height="600"
>
</div>
<style>
.lazy-image.blur {
filter: blur(20px);
transition: filter 0.3s;
}
.lazy-image.loaded {
filter: blur(0);
}
</style>
フォントの最適化
// Webフォント最適化
const fontOptimization = {
// 1. font-display: swap で描画をブロックしない
cssRule: `
@font-face {
font-family: 'NotoSansJP';
src: url('/fonts/NotoSansJP.woff2') format('woff2');
font-display: swap;
unicode-range: U+3000-9FFF; /* 日本語の範囲のみ */
}
`,
// 2. preload でフォントを先読み
preload: '<link rel="preload" href="/fonts/NotoSansJP.woff2" as="font" type="font/woff2" crossorigin>',
// 3. サブセット化(使用する文字のみ含める)
subset: '不要な文字を除去してファイルサイズを80%削減',
};
CSS/JSの最適化
| 技法 | 説明 | 効果 |
|---|---|---|
| Minification | 空白・コメント除去 | 20-40%削減 |
| Compression | gzip/brotli圧縮 | さらに60-80%削減 |
| Critical CSS | 初期表示に必要なCSSをインライン化 | レンダリングブロック回避 |
| Preconnect | 外部ドメインへの事前接続 | DNS+TCP+TLS時間を節約 |
<!-- Critical CSS のインライン化 -->
<head>
<!-- 初期表示に必要なCSSをインライン -->
<style>
.header { background: #1e293b; color: white; padding: 16px; }
.hero { min-height: 400px; display: flex; align-items: center; }
</style>
<!-- 残りのCSSは非同期読み込み -->
<link rel="preload" href="/css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<!-- 外部ドメインへの事前接続 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
</head>
まとめ
| ポイント | 内容 |
|---|---|
| 画像フォーマット | WebP/AVIFを優先、フォールバックにJPEG |
| レスポンシブ画像 | デバイスに応じたサイズ・フォーマットを配信 |
| 圧縮 | 適切なquality設定で80-90%削減可能 |
| フォント | サブセット化 + font-display: swap |
| CSS/JS | Minification + Compression + Critical CSS |
チェックリスト
- 画像フォーマットの使い分けを理解した
- レスポンシブ画像の実装方法を把握した
- LQIPの概念と実装を理解した
- フォントとCSS/JSの最適化手法を知った
次のステップへ
次は「CDNとエッジコンピューティング」を学びます。コンテンツをユーザーの近くから配信し、グローバルなパフォーマンスを向上させる方法です。
推定読了時間: 30分