記事一覧に戻る

Webパフォーマンス最適化の実践テクニック2024

6分で読む
Tech Blog Admin
Webパフォーマンス最適化の実践テクニック2024
広告スペース
Slot: article-top
Format: horizontal
本番環境でGoogle AdSenseが表示されます

なぜWebパフォーマンスが重要なのか

Webサイトの表示速度は、ユーザー体験とビジネス成果に直接影響します。Googleの調査によると、ページの読み込み時間が1秒から3秒に増加すると、直帰率が32%上昇します。

Core Web Vitalsの理解と改善

LCP (Largest Contentful Paint)

最大のコンテンツが表示されるまでの時間。目標は2.5秒以内。

// LCPの改善例
// ❌ 悪い例
<img src="/hero-image.jpg" />

// ✅ 良い例
<img 
  src="/hero-image.jpg" 
  loading="eager"
  fetchpriority="high"
  width="1200"
  height="600"
/>

FID (First Input Delay) → INP (Interaction to Next Paint)

ユーザーの入力から応答までの遅延。2024年3月からINPに置き換わりました。

// INPの改善:重い処理を分割
// ❌ 悪い例
function processLargeData(data) {
  for (let i = 0; i < data.length; i++) {
    // 重い処理
    complexCalculation(data[i]);
  }
}

// ✅ 良い例
async function processLargeData(data) {
  const chunkSize = 100;
  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    
    // メインスレッドを解放
    await new Promise(resolve => setTimeout(resolve, 0));
    
    chunk.forEach(item => complexCalculation(item));
  }
}

CLS (Cumulative Layout Shift)

視覚的な安定性。目標は0.1以下。

/* CLSの改善 */
/* ❌ 悪い例 */
.image {
  /* サイズ指定なし */
}

/* ✅ 良い例 */
.image-container {
  aspect-ratio: 16 / 9;
  width: 100%;
  overflow: hidden;
}

/* またはHTMLで指定 */
<img src="..." width="800" height="450" />

画像最適化のベストプラクティス

1. 次世代画像フォーマットの活用

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Description" loading="lazy">
</picture>

2. Next.js Image コンポーネントの活用

import Image from 'next/image';

function OptimizedImage() {
  return (
    <Image
      src="/hero.jpg"
      alt="Hero image"
      width={1200}
      height={600}
      placeholder="blur"
      blurDataURL={blurDataUrl}
      priority // LCP画像の場合
      sizes="(max-width: 768px) 100vw,
             (max-width: 1200px) 50vw,
             33vw"
    />
  );
}

3. 画像の遅延読み込み

// Intersection Observer を使用した実装
const lazyImages = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.add('loaded');
      imageObserver.unobserve(img);
    }
  });
}, {
  rootMargin: '50px 0px' // 50px前から読み込み開始
});

lazyImages.forEach(img => imageObserver.observe(img));

JavaScriptの最適化

1. Code Splitting

// 動的インポート
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <Skeleton />,
  ssr: false // 必要に応じて
});

// Route-based splitting
const routes = [
  {
    path: '/dashboard',
    component: lazy(() => import('./pages/Dashboard'))
  }
];

2. Tree Shaking

// ❌ 悪い例
import _ from 'lodash';
const result = _.debounce(fn, 300);

// ✅ 良い例
import debounce from 'lodash/debounce';
const result = debounce(fn, 300);

3. Web Workers の活用

// worker.js
self.addEventListener('message', (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
});

// main.js
const worker = new Worker('/worker.js');
worker.postMessage(data);
worker.addEventListener('message', (e) => {
  console.log('Result:', e.data);
});

CSSの最適化

1. Critical CSS

<!-- インラインでクリティカルCSSを配置 -->
<style>
  /* ファーストビューに必要なCSS */
  .hero { ... }
  .header { ... }
</style>

<!-- 残りのCSSは非同期で読み込み -->
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

2. CSS-in-JS の最適化

// Emotion with SSR
import { CacheProvider } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';

const cache = createEmotionCache();
const { extractCriticalToChunks, constructStyleTagsFromChunks } = 
  createEmotionServer(cache);

// サーバーサイド
const emotionChunks = extractCriticalToChunks(html);
const emotionCss = constructStyleTagsFromChunks(emotionChunks);

ネットワーク最適化

1. Resource Hints

<!-- DNS Prefetch -->
<link rel="dns-prefetch" href="//api.example.com">

<!-- Preconnect -->
<link rel="preconnect" href="https://fonts.googleapis.com">

<!-- Prefetch -->
<link rel="prefetch" href="/next-page.js">

<!-- Preload -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>

2. Service Worker でキャッシュ戦略

// Cache First 戦略
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request).then((response) => {
        return caches.open('v1').then((cache) => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

パフォーマンス計測と監視

1. Web Vitals の計測

import { getCLS, getFID, getLCP, getTTFB, getFCP } from 'web-vitals';

function sendToAnalytics(metric: any) {
  // Google Analytics 4
  gtag('event', metric.name, {
    value: Math.round(metric.value),
    metric_id: metric.id,
    metric_value: metric.value,
    metric_delta: metric.delta,
  });
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
getFCP(sendToAnalytics);

2. Performance Observer

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
});

observer.observe({ type: 'largest-contentful-paint', buffered: true });

チェックリスト

✅ 画像の最適化(フォーマット、サイズ、遅延読み込み)
✅ Critical CSSの実装
✅ JavaScriptのCode Splitting
✅ フォントの最適化(font-display: swap)
✅ HTTP/2 または HTTP/3の使用
✅ CDNの活用
✅ Gzip/Brotli圧縮
✅ キャッシュ戦略の実装
✅ Third-partyスクリプトの最適化
✅ Core Web Vitalsの定期的な計測

まとめ

Webパフォーマンスの最適化は継続的なプロセスです。Core Web Vitalsを指標として、ユーザー体験を向上させることで、ビジネス成果にも貢献できます。

定期的な計測と改善を繰り返し、高速なWebサイトを維持しましょう!

広告スペース
Slot: article-bottom
Format: rectangle
本番環境でGoogle AdSenseが表示されます

関連記事