404 Not Found

404 Not Found


nginx

パフォーマンス最適化

MegaShopは公開されましたが, Lighthouseスコアはわずか45です。初回画面の読み込みに4.5秒かかり, 画像は最適化されておらず, JSバンドルが大きすぎます。CharlieはLighthouseスコア90+と初回画面のLCP 2秒未満を達成する必要があります。

1. 学ぶ内容


2. アーキテクトのある本当の話

(1) 課題:Lighthouseスコア45, 初回コンテンツ描画4.5秒

AliceがスマホでMegaShopを開くと, 商品が表示されるまで4.5秒かかりました。CharlieがLighthouse監査を実行すると, JSバンドルが2.8 MB, すべての画像が未圧縮のPNG, キャッシュヘッダーがなく, 50コンポーネントのコードが初回画面で読み込まれていました。

(2) Nuxt 3パフォーマンス最適化の解決策

Nuxt 3にはさまざまな組み込みパフォーマンス最適化機能が含まれています:

TYPESCRIPT
// @nuxt/imageによる画像最適化
<NuxtImg src="/products/123.webp" width="400" height="400" loading="lazy" />

(3) 結果:Lighthouseスコア92, LCP 1.3秒

最適化後, JSバンドルサイズは180 KB (gzip)に削減され, 画像は自動的にWebPで配信されレスポンシブ対応, 初回画面のLCPは1.3秒, Lighthouseスコアは92になりました。


3. コード分割とTree Shaking

(1) パフォーマンス最適化判定ツリー

100%
flowchart TB
    A[パフォーマンス問題] --> B{問題の種類は?}
    B -->|初回描画が遅い| C[コード分割]
    B -->|画像が大きい| D[画像最適化]
    B -->|後続読み込みが遅い| E[キャッシング戦略]
    B -->|JSが大きすぎる| F[Tree Shaking]

    C --> C1[Lazyコンポーネント]
    C --> C2[動的インポート]
    D --> D1["@nuxt/imageモジュール"]
    D --> D2[WebP変換]
    E --> E1[HTTPキャッシュヘッダー]
    E --> E2[Nitro KVキャッシュ]
    F --> F1[rollup-plugin-visualizerでバンドル確認]

(2) バンドル分析

BASH
npm install -D rollup-plugin-visualizer
TYPESCRIPT
// nuxt.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineNuxtConfig({
  vite: {
    plugins: [
      visualizer({ filename: 'bundle-stats.html', open: true })
    ],
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'vendor': ['vue', 'vue-router'],
            'pinia': ['pinia']
          }
        }
      }
    }
  }
})

(1) ▶ サンプル:Lazyコンポーネントによるコード分割

VUE
<template>
  <div>
    <!-- コア:即座に読み込み -->
    <AppHeader />
    <ProductHero :product="featured" />

    <!-- フォールド下:遅延読み込み -->
    <LazyProductGrid :products="all" />
    <LazyProductReviews :product-id="id" />

    <!-- 条件付き:表示時のみ読み込み -->
    <LazyNewsletterSignup v-if="showNewsletter" />
  </div>
</template>

出力:

TEXT
// 実行成功

(3) 最適化前後の比較

指標 最適化前 最適化後 改善
JSバンドル (gzip) 2.8 MB 180 KB -94%
初回画面のコンポーネント数 50 8 -84%
初回画面のJSダウンロード 2.8 MB 85 KB -97%
LCP 4.5s 1.3s -71%

4. 画像最適化

(1) ▶ サンプル:@nuxt/imageのインストール

BASH
npm install @nuxt/image

出力:

TEXT
# コマンド実行成功
TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/image'],
  image: {
    quality: 80,
    format: ['webp', 'avif'],
    screens: {
      xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, xxl: 1536
    }
  }
})

(2) ▶ サンプル:NuxtImgレスポンシブ画像

VUE
<template>
  <div>
    <!-- 自動WebP/AVIF + レスポンシブsrcset -->
    <NuxtImg
      src="/images/product-123.jpg"
      width="800"
      height="600"
      format="webp"
      loading="lazy"
      sizes="sm:100vw md:50vw lg:33vw"
      :modifiers="{ quality: 80 }"
      alt="Premium Headphones"
    />

    <!-- ブラー付きプレースホルダー -->
    <NuxtImg
      src="/images/hero.jpg"
      placeholder
      width="1920"
      height="1080"
      format="webp"
      alt="MegaShop Hero"
    />
  </div>
</template>

出力:

TEXT
// 実行成功

(3) ▶ サンプル:NuxtPictureマルチフォーマット

VUE
<template>
  <!-- NuxtPicture:ブラウザごとに最適フォーマットを自動選択 -->
  <NuxtPicture
    src="/images/banner.jpg"
    width="1200"
    height="400"
    format="avif,webp"
    loading="lazy"
    alt="Sale Banner"
  />
</template>

出力:

TEXT
// 実行成功

(1) 画像フォーマットの比較

フォーマット 圧縮率 ブラウザサポート ユースケース
AVIF 🟢 最高 (JPG比50%) Chrome/Edge/Firefox モダンブラウザ
WebP 🟢 高 (JPG比30%) 主要ブラウザ全般 汎用推奨フォーマット
JPEG 🟡 ベースライン 全ブラウザ 互換性フォールバック
PNG 🔴 大きい 全ブラウザ 透過画像

5. キャッシング戦略

(1) ▶ サンプル:HTTPキャッシュヘッダー

TYPESCRIPT
// nuxt.config.ts - ルートごとのキャッシュヘッダー
export default defineNuxtConfig({
  routeRules: {
    // 静的アセット:1年間のimmutableキャッシュ
    '/_nuxt/**': {
      headers: { 'cache-control': 'public, max-age=31536000, immutable' }
    },
    // 画像:1日キャッシュ
    '/images/**': {
      headers: { 'cache-control': 'public, max-age=86400, stale-while-revalidate=604800' }
    },
    // 商品ページ:ISRキャッシュ
    '/products/**': {
      swr: 86400,
      headers: { 'cache-control': 'public, s-maxage=86400, stale-while-revalidate=3600' }
    }
  }
})

出力:

TEXT
// 実行成功

(2) ▶ サンプル:Nitro KVキャッシュAPI

TYPESCRIPT
// server/api/products/[id].get.ts - キャッシュ付きハンドラー
export default cachedEventHandler(
  async (event) => {
    const id = getRouterParam(event, 'id')
    const product = await fetchProductFromDB(Number(id))
    return product
  },
  {
    maxAge: 60 * 60,        // 1時間キャッシュ
    swr: true,               // キャッシュ期限後も古いデータを返しつつ再検証
    getKey: (event) => `product:${getRouterParam(event, 'id')}`,
    varies: ['Accept-Language'] // 言語ごとに異なるキャッシュ
  }
)

出力:

TEXT
// 実行成功

(1) キャッシュ階層の比較

レベル 場所 速度 制御方法
ブラウザキャッシュ ユーザーデバイス ⚡⚡⚡ 最速 Cache-Controlヘッダー
CDNエッジキャッシュ 最寄りのノード ⚡⚡ 高速 CDN設定 + Vary
Nitro KVキャッシュ サーバーメモリ ⚡ 高速 cachedEventHandler
ISRページキャッシュ Nitroストレージ ⚡ 高速 routeRules swr

6. 総合例:MegaShopパフォーマンス最適化設定

TYPESCRIPT
// nuxt.config.ts - 完全パフォーマンス最適化
export default defineNuxtConfig({
  ssr: true,

  modules: ['@nuxt/image', '@nuxtjs/tailwindcss'],

  // 画像最適化
  image: {
    quality: 80,
    format: ['webp', 'avif'],
    screens: { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280 }
  },

  // Viteビルド最適化
  vite: {
    build: {
      cssCodeSplit: true,
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes('node_modules')) {
              if (id.includes('pinia')) return 'vendor-pinia'
              if (id.includes('vue')) return 'vendor-vue'
              return 'vendor-libs'
            }
          }
        }
      }
    }
  },

  // ルートレベルのキャッシング戦略
  routeRules: {
    '/': { prerender: true },
    '/products': { swr: 3600 },
    '/products/**': { swr: 86400 },
    '/admin/**': { ssr: false },
    '/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
    '/images/**': { headers: { 'cache-control': 'public, max-age=86400' } }
  },

  // アプリパフォーマンスヒント
  experimental: {
    payloadExtraction: true // ペイロードサイズを削減
  }
})
VUE
<!-- pages/index.vue - 最適化されたホームページ -->
<template>
  <div>
    <AppHeader />

    <!-- フォールド上:即座に読み込み -->
    <section class="hero">
      <NuxtImg src="/images/hero.jpg" width="1200" height="400" format="webp"
        priority alt="MegaShop" />
      <h1>MegaShop</h1>
    </section>

    <!-- フォールド下:遅延読み込み -->
    <LazyProductGrid :products="featured" />

    <!-- 条件付き:ビューポート内のみ読み込み -->
    <LazyNewsletterSignup v-if="showNewsletter" />
  </div>
</template>

<script setup lang="ts">
const { data: featured } = await useFetch('/api/products/featured')
const showNewsletter = ref(false)
</script>

❓ よくある質問

Q Lighthouseスコアが低い場合はどうすればよいですか?
A 優先度順に対処してください:1)画像最適化 (WebP/圧縮/遅延読み込み)→ 2)JSバンドル分析 (Lazyコンポーネント/Tree Shaking)→ 3)キャッシング戦略 (HTTPヘッダー/ISR)→ 4)フォント最適化。
Q @nuxt/imageは外部画像URLをサポートしていますか?
A はい。image.domainsで外部ドメインを追加します:image: { domains: ['cdn.example.com'] }。NuxtImgは外部画像も自動的に最適化します。
Q NuxtImgと通常のimgタグの違いは何ですか?
A NuxtImgは自動的にsrcsetを生成し, 画像をWebP/AVIFに変換し, デバイスサイズに合わせてクロップし, 遅延読み込みを有効にします。通常のimgタグでは, これらすべての最適化を手動で行う必要があります。
Q ISRキャッシングとHTTPキャッシングはどちらが優先されますか?
A ISRはサーバー側キャッシュ (Nitroに保存), HTTPキャッシングはブラウザ/CDN層で発生します。リクエストフロー:ブラウザキャッシュ → CDNキャッシュ → Nitro ISR → 動的レンダリング。各層は独立して動作します。
Q manualChunksの設定で注意すべき落とし穴はありますか?
A クライアントチャンクにサーバー側コードを含めないでください。Nuxt 3はサーバーとクライアントのコード分離を自動的に処理します。manualChunksはサードパーティライブラリの分割にのみ使用してください。
Q Lighthouseスコア90+を達成するのは難しいですか?
A SSR, 画像最適化, キャッシング戦略で90+に到達できます。最も難しいのはLCPを2.5秒未満に保つことで, クリティカルレンダリングパスを合理化し, 初回画面では必須リソースのみを読み込む必要があります。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度:⭐):@nuxt/imageをインストールし, すべてのimgタグをNuxtImgに置き換え, 自動WebP変換を確認してください。
  2. 応用問題 (難易度:⭐⭐):rollup-plugin-visualizerでバンドルを分析し, 最大の依存関係を特定してmanualChunksで分割を設定してください。
  3. チャレンジ (難易度:⭐⭐⭐):Lighthouseスコア90+を達成してください:画像最適化 + コード分割 + キャッシング戦略, Lighthouse CIで自動監査

---|

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%