ウェブパフォーマンス最適化:サイトを高速化する
· 12分で読めます
目次
ウェブサイトのパフォーマンスは、単にサイトを速く感じさせるだけではありません。収益に直接影響します。調査によると、ページ読み込み時間が1秒遅れるとコンバージョンが7%減少し、モバイルユーザーの53%が3秒以上かかるサイトを離脱します。
この包括的なガイドでは、Core Web Vitalsの理解から高度なキャッシング技術の実装まで、ウェブサイトのパフォーマンスを最適化するための実証済みの戦略を説明します。eコマースストア、コンテンツサイト、ウェブアプリケーションのいずれを運営していても、これらの技術はユーザーにより速く、より応答性の高い体験を提供するのに役立ちます。
Core Web Vitalsの解説
Core Web Vitalsは、ウェブ上のユーザー体験を測定するためのGoogleの標準化された指標です。2021年以降、Googleの検索アルゴリズムのランキング要因となっており、SEOとユーザー満足度の両方にとって不可欠です。
これらの指標は、ユーザー体験の3つの重要な側面に焦点を当てています:読み込みパフォーマンス、インタラクティブ性、視覚的安定性。各指標を分解し、実用的な最適化戦略を探りましょう。
LCP — Largest Contentful Paint
目標:2.5秒未満
LCPは、最大の可視コンテンツ要素が画面にレンダリングされるまでの時間を測定します。これは通常、ヒーロー画像、メイン見出し、またはビデオプレーヤーです。ページが最初に読み込まれたときにビューポートを支配するものです。
LCPを遅くする一般的な原因は次のとおりです:
- 遅いサーバー応答時間(高いTTFB)
- レンダリングをブロックするJavaScriptとCSS
- 大きく最適化されていない画像
- コンテンツを遅延させるクライアントサイドレンダリング
最適化戦略:
- 重要なリソースをプリロード: ブラウザにLCP要素をすぐに取得するように指示します
<link rel="preload" as="image" href="hero.webp">
<link rel="preload" as="font" href="main-font.woff2" crossorigin>
- サーバー応答時間の最適化: より高速なホスティングの使用、サーバーサイドキャッシングの実装、データベースクエリの最適化により、TTFBを600ms未満にすることを目指します
- CDNを使用: ユーザーに近いエッジロケーションから静的アセットを提供します
- レンダリングをブロックするリソースを排除: 重要なCSSをインライン化し、重要でないJavaScriptを遅延させます
- 画像を最適化: WebPやAVIFなどの最新フォーマットを使用し、積極的に圧縮し、レスポンシブ画像を提供します
プロのヒント: Lighthouse Analyzerを使用してLCP要素を特定し、何が高速読み込みをブロックしているかを正確に確認してください。
INP — Interaction to Next Paint
目標:200ミリ秒未満
INPは、応答性のより包括的な測定として、2024年にFirst Input Delay(FID)に取って代わりました。ページのライフサイクル全体を通じて、すべてのユーザーインタラクション(クリック、タップ、キーボード入力)の遅延を追跡します。
INPスコアが低い原因は通常次のとおりです:
- メインスレッドをブロックする長時間実行されるJavaScriptタスク
- 実行に時間がかかりすぎる重いイベントハンドラー
- 過度なDOM操作
- CPU時間を独占するサードパーティスクリプト
最適化戦略:
- 長いタスクを分割: 50msを超えるJavaScriptタスクは、より小さなチャンクに分割する必要があります
// すべてを一度に処理する代わりに
function processItems(items) {
items.forEach(item => heavyOperation(item));
}
// チャンクに分割する
async function processItems(items) {
for (let i = 0; i < items.length; i++) {
heavyOperation(items[i]);
if (i % 50 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
- Web Workersを使用: 重い計算をバックグラウンドスレッドにオフロードします
- 高コストなハンドラーをデバウンス: 迅速なユーザー入力中にイベントハンドラーが起動する頻度を制限します
- サードパーティスクリプトを最適化: 非同期で読み込み、タグマネージャーを使用して実行を制御することを検討します
- requestIdleCallbackを使用: ブラウザのアイドル時間中に重要でない作業をスケジュールします
CLS — Cumulative Layout Shift
目標:0.1未満
CLSは、ページ読み込み中の予期しないレイアウトシフトを追跡することで視覚的安定性を測定します。ボタンをクリックしようとしたときに、上に広告が読み込まれたためにボタンが最後の瞬間に移動するほど、ユーザーをイライラさせるものはありません。
レイアウトシフトの一般的な原因:
- 寸法のない画像とビデオ
- 動的に挿入されるコンテンツ(広告、埋め込み)
- FOIT/FOUTを引き起こすウェブフォント
- レイアウトの再計算をトリガーするアニメーション
最適化戦略:
- 常に寸法を指定: すべてのメディアに明示的な幅と高さの属性を設定します
<img src="product.jpg" width="800" height="600" alt="Product">
<video width="1920" height="1080" poster="thumbnail.jpg">
- 動的コンテンツ用のスペースを確保: CSSのaspect-ratioまたはmin-heightを使用します
.ad-container {
min-height: 250px;
aspect-ratio: 16 / 9;
}
- フォントをプリロード: フォントの入れ替えによるレイアウトシフトを防ぎます
- CSS containmentを使用: レイアウトの変更を特定の要素に分離します
- 既存のコンテンツの上にコンテンツを挿入しない: フォールドの下に新しい要素を追加するか、オーバーレイを使用します
画像最適化戦略
画像は通常、ページの総重量の50〜70%を占めており、パフォーマンス向上の最大の機会となっています。最新の画像最適化は、単にJPEGを圧縮するだけではありません。
適切なフォーマットの選択
異なる画像フォーマットは異なるユースケースで優れています。包括的な比較は次のとおりです:
| フォーマット | 最適な用途 | 圧縮 | ブラウザサポート |
|---|---|---|---|
| WebP | 写真、複雑なグラフィック | JPEGより25〜35%小さい | 96%(すべての最新ブラウザ) |
| AVIF | 写真、高品質画像 | JPEGより50%小さい | 88%(Chrome、Firefox、Safari 16+) |
| JPEG | 写真のフォールバック | ベースライン圧縮 | 100% |
| PNG | 透明度、シンプルなグラフィック | ロスレス、大きなファイル | 100% |
| SVG | アイコン、ロゴ、イラスト | スケーラブル、非常に小さい | 100% |
<picture>要素を使用して、フォールバック付きの最新フォーマットを提供します:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero image" width="1200" height="600">
</picture>
レスポンシブ画像
モバイルユーザーに同じ2000pxの画像を提供するのは無駄です。srcsetとsizesを使用して、ブラウザが最適な画像サイズを選択できるようにします:
<img
srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
src="medium.jpg"
alt="Responsive image">
これはブラウザに次のように伝えます:「3つのバージョンがあります。600px以下の画面では400pxバージョンを使用してください。1000px以下の画面では800pxを使用してください。それ以外の場合は1200pxを使用してください。」
圧縮技術
積極的な圧縮により、品質の損失を最小限に抑えながらファイルサイズを60〜80%削減できます:
- JPEG: ほとんどの写真には品質80〜85を使用します(品質90+はほとんど知覚できません)
- PNG: pngquantやTinyPNGなどのツールを使用してカラーパレットを削減します
- WebP: 非可逆圧縮には品質75〜80を使用します
- AVIF: 品質60〜70を使用します(AVIFの圧縮はより効率的です)
Image Optimizerを試して、異なるフォーマットと品質設定をバッチ処理して比較してください。
クイックヒント: 「Save-Data」モード検出を有効にして、低速接続のユーザーにさらに圧縮された画像を提供します:if (navigator.connection?.saveData) { /* より低品質を提供 */ }
遅延読み込みの実装
遅延読み込みは、画面外のリソースの読み込みを必要になるまで延期し、初期ページの重量を劇的に削減し、読み込み時間を改善します。
ネイティブ遅延読み込み
最新のブラウザは、シンプルな属性でネイティブ遅延読み込みをサポートしています:
<img src="image.jpg" loading="lazy" alt="Description">
<iframe src="embed.html" loading="lazy"></iframe>
これは、95%以上のブラウザサポートで画像とiframeに機能します。ブラウザは、リソースがビューポートに近づくと自動的に読み込みます。
即座読み込みを使用する場合:
- ファーストビューの画像(特にLCP要素)
- 重要なUI要素
- パフォーマンスに影響しない小さな画像
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="Hero">
JavaScriptベースの遅延読み込み
より細かい制御や古いブラウザのサポートには、Intersection Observer APIを使用します:
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img.lazy').forEach(img => {
imageObserver.observe(img);
});
HTML構造:
<img data-src="actual-image.jpg" src="placeholder.jpg" class="lazy" alt="Description">
遅延読み込みのベストプラクティス
- プレースホルダーを使用: レイアウトシフトを防ぐために、低品質画像プレースホルダー(LQIP)または単色を表示します
- 適切なしきい値を設定: ビューポートに入る200〜300px前に画像の読み込みを開始します
- サードパーティの埋め込みを遅延読み込み: YouTube動画、ソーシャルメディアウィジェット、マップは重いです。インタラクション時に読み込みます
- すべてを遅延読み込みしない: ファーストビューのコンテンツはすぐに読み込む必要があります
コードの圧縮とバンドル化
圧縮は、機能を変更せずにコードから不要な文字を削除し、バンドル化は複数のファイルを結合してHTTPリクエストを削減します。
CSSの最適化
CSSファイルは、特にフレームワークを使用する場合、驚くほど大きくなる可能性があります。最適化する方法は次のとおりです:
- CSSを圧縮: 空白、コメント、冗長なコードを削除します
- 未使用のCSSを削除: PurgeCSSなどのツールは、使用していないスタイルを排除します
- クリティカルCSS: ファーストビューのスタイルをインライン化し、残りを遅延させます
<style>
/* クリティカルCSSをここにインライン化 */
.header { background: #38bdf8; }
.hero { min-height: 400px; }
</style>
<link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="main.css"></noscript>
JavaScriptの最適化
JavaScriptは処理するのに最もコストのかかるリソースです。ダウンロード、解析、コンパイル、実行する必要があります。
圧縮戦略:
- Terserやesbuildなどのツールを使用してJavaScriptを圧縮します
- ツリーシェイキングを有効にしてデッドコードを削除します
- コードをチャンクに分割し、オンデマンドで読み込みます
動的インポートを使用したコード分割の例:
// すべてを事前にインポートする代わりに
import { heavyLibrary } from './heavy-library.js';
// 必要なときにのみ読み込む
button.addEventListener('click', async () => {
const { heavyLibrary } = await import('./heavy-library.js');
heavyLibrary.doSomething();
});
ビルドツールの設定
最新のビルドツールは自動的に圧縮を処理します。Vite設定のサンプルは次のとおりです:
// vite.config.js
export default {
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
},
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'date-fns']
}
}
}
}
}
プロのヒント: Bundle Analyzerを使用してJavaScriptバンドルを視覚化し、最適化の機会を特定してください