• #nuxt
  • #performance
  • #prefetch
  • #NuxtLink

NuxtLinkのprefetchとは何か:理解と最適な設定

prefetchとは

prefetch(プリフェッチ) とは、ユーザーがリンクをクリックする前に、リンク先のデータを事前に取得しておく機能。

目的

ユーザーがリンクをクリックした時に、即座にページ遷移できるようにする。

従来の遷移:
クリック → データ取得開始 → 取得完了 → 表示
                ↑ この待ち時間がある

prefetchあり:
[事前にデータ取得完了] → クリック → 即座に表示

NuxtLinkのデフォルト動作

prefetchはデフォルトで有効

<!-- これは prefetch="true" と同じ -->
<NuxtLink to="/blog/article-name">記事タイトル</NuxtLink>

発動条件

リンクがビューポート(画面内)に入った時に自動的にプリフェッチが開始される。

取得されるもの

  1. _payload.json: ページのデータ(propsなど)
  2. JSチャンク: ページコンポーネントのJavaScript
  3. CSSチャンク: ページ固有のスタイル

あるべき状態

ケース1: 少数のリンク(5件以下)

prefetch有効でOK

<!-- ナビゲーションメニューなど -->
<NuxtLink to="/about">About</NuxtLink>
<NuxtLink to="/contact">Contact</NuxtLink>

理由:リクエスト数が少ないので問題にならない。

ケース2: 多数のリンク(10件以上)

prefetch無効を推奨

<!-- ブログ一覧など -->
<NuxtLink
  v-for="article in articles"
  :key="article.path"
  :to="article.path"
  :prefetch="false"
>
  {{ article.title }}
</NuxtLink>

理由:大量の同時リクエストがネットワークを詰まらせる。

ケース3: 遷移確率が高いリンク

prefetch有効でOK

<!-- 「次の記事」ボタンなど -->
<NuxtLink to="/blog/next-article" prefetch>
  次の記事へ
</NuxtLink>

理由:ユーザーがクリックする可能性が高い。

ケース4: 遷移確率が低いリンク

prefetch無効を推奨

<!-- フッターのリンクなど -->
<NuxtLink to="/privacy" :prefetch="false">
  プライバシーポリシー
</NuxtLink>

理由:ほとんどクリックされないのに帯域を消費する。


nuxt.config.tsでのグローバル設定

prefetch戦略の変更

// nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    // ペイロード抽出を無効化(prefetchされるデータを減らす)
    payloadExtraction: false
  }
})

routeRulesで特定ルートのみ設定

export default defineNuxtConfig({
  routeRules: {
    // ブログ記事はprefetchしない
    '/blog/**': { experimentalNoScripts: true }
  }
})

今回の事例:ブログ一覧の過剰プリフェッチ

問題

/blogページから記事への遷移に約1秒のラグがあった。

原因

Chrome DevToolsで確認したところ、ページ遷移時に65個のリクエストが発生:

/blog/openai-vs-google-considerations/_payload.json
/blog/nvidia-moat/_payload.json
/blog/tax-consultation-form/_payload.json
/blog/tax-accountant-inquiry/_payload.json
/blog/drawio-viewer-test/_payload.json
/blog/blood-medicine-world/_payload.json
... 他にも多数

各リンクに対して:

  • _payload.json(ページデータ)
  • .css(スタイル)
  • .js(スクリプト)

が個別にリクエストされていた。

なぜ遅くなるか

  1. ネットワーク帯域の競合: 同時リクエストが帯域を奪い合う
  2. HTTP/2の同時接続制限: 同一ドメインへの同時リクエスト数に上限がある
  3. メインスレッドの負荷: レスポンス処理でCPUを消費

現在の実装(問題あり)

<!-- ArticleTable.vue -->
<NuxtLink :to="article.path" class="article-link">
  {{ article.title }}
</NuxtLink>

<!-- BlogCalendar.vue -->
<NuxtLink :to="article.path" class="article-link">
  {{ article.title }}
</NuxtLink>

両方ともprefetch未設定 → デフォルトでtrue → 過剰なプリフェッチ

修正案

<!-- ArticleTable.vue -->
<NuxtLink :to="article.path" :prefetch="false" class="article-link">
  {{ article.title }}
</NuxtLink>

<!-- BlogCalendar.vue -->
<NuxtLink :to="article.path" :prefetch="false" class="article-link">
  {{ article.title }}
</NuxtLink>

判断フローチャート

リンクの数は?
  │
  ├─ 5件以下 → prefetch有効でOK
  │
  └─ 10件以上
       │
       ├─ ユーザーが必ずクリックする → prefetch有効
       │  (例:「次へ」ボタン)
       │
       └─ どれがクリックされるか不明 → prefetch無効
          (例:記事一覧)

まとめ

シチュエーションprefetch設定理由
ナビゲーションメニュー有効(デフォルト)リンク数が少なく、頻繁にクリックされる
ブログ記事一覧無効大量リンクで帯域を消費
カレンダービュー無効表示セルが多い(42セル)
「次の記事」ボタン有効クリック確率が高い
フッターリンク無効クリック確率が低い
ページネーション有効/無効どちらでも状況による

基本方針: 一覧ページでは:prefetch="false"を付ける習慣をつける。


参考リンク