Nuxtにおけるサーバーサイドデータフェッチング
Nuxtアプリケーションを構築していて、データが2回フェッチされる理由や、同じキーを共有するコンポーネントが予期しない動作をする理由に悩んでいるなら、あなただけではありません。Nuxt SSRのデータフェッチングには、経験豊富な開発者でさえつまずく特有のルールがあります。
この記事では、Nuxt 4におけるuseAsyncDataとuseFetchの動作について、ペイロードハイドレーション、ナビゲーション動作、キー管理、そして最も混乱を招く落とし穴について解説します。
重要なポイント
- Nuxtは
useFetchとuseAsyncDataをサーバー上で実行し、レスポンスをHTMLペイロードにシリアライズして、クライアント側で再フェッチせずにハイドレーションを行います - 同じキーを共有するコンポーネントは同一のリアクティブステートを共有します—独立したデータインスタンスには異なるキーを使用してください
- 現在のNuxt 4リリースでは、特定のオプション(handler、transform、pick、getCachedData、default、deep)は、キーを共有する呼び出し間で一致する必要があります
- SSRセーフなフェッチングには
useFetchまたはuseAsyncDataを使用し、$fetchはイベントハンドラーとクライアント専用コードに限定してください
Nuxtがサーバーサイドデータフェッチングを実行する仕組み
ページやコンポーネントでuseFetchまたはuseAsyncDataを呼び出すと、Nuxtは初回リクエスト時にサーバー上でそのフェッチを実行します。サーバーはレスポンスをHTMLに埋め込まれたペイロードにシリアライズします。クライアントがハイドレーションする際、再フェッチする代わりにこのペイロードを読み取ります—重複するネットワークリクエストを排除します。
const { data } = await useFetch('/api/products')
この1行のコードはサーバー上で実行され、結果をページに埋め込み、クライアント側でシームレスにハイドレーションされます。二重フェッチはありません。ハイドレーションミスマッチもありません。
ブロッキングフェッチと遅延フェッチ
デフォルトでは、Nuxtはawaitされたデータフェッチングが完了するまでナビゲーションをブロックします。これにより、ページがすでにデータを利用可能な状態でレンダリングされることが保証されます。
クライアントサイドナビゲーションでは、遅延フェッチングを選択できます:
const { data, status } = useLazyFetch('/api/comments')
遅延フェッチングでは、フェッチがバックグラウンドで実行される間、デフォルトではナビゲーションはブロックされません。status refを使用してテンプレート内でローディング状態を処理する必要があります。
Nuxtデータフェッチングキーの理解
キーは、Nuxtがリクエストをキャッシュして重複排除する方法の中心です。すべてのuseFetch呼び出しは、URLをデフォルトのキーとして使用します。useAsyncDataの場合、キーを明示的に指定するか、Nuxtに決定論的なキーを生成させます。
重要な点は次のとおりです:同じキーを共有するコンポーネントは同じステートを共有します。これにはdata、error、status、pendingのrefが含まれます。
// コンポーネントA
const { data } = await useAsyncData('users', () => $fetch('/api/users'))
// コンポーネントB - コンポーネントAとステートを共有
const { data } = await useAsyncData('users', () => $fetch('/api/users'))
両方のコンポーネントは同一のリアクティブrefを受け取ります。一方を変更すると、もう一方にも反映されます。
キー一貫性ルール
現在のNuxt 4バージョンでは、複数の呼び出しがキーを共有する場合、Nuxtは特定のオプションの一貫性を強制します。これらは一致する必要があります:
- ハンドラー関数
transform関数pick配列getCachedData関数default値deepオプション
これらは安全に異なることができます:
serverlazyimmediatededupewatch
一貫性に違反すると、開発時の警告と予測不可能な動作が発生します。
Discover how at OpenReplay.com.
安全なキー戦略
ルート固有のデータの場合、キーにルートパラメータを含めます:
const route = useRoute()
const { data } = await useAsyncData(
`product-${route.params.id}`,
() => $fetch(`/api/products/${route.params.id}`)
)
ステートを共有すべきでない独立したインスタンスの場合、異なるキーを使用します:
const { data: sidebar } = await useAsyncData('users-sidebar', fetchUsers)
const { data: main } = await useAsyncData('users-main', fetchUsers)
Nuxtデータキャッシングと重複排除動作
Nuxtは、一致するキーを持つ同時リクエストを自動的に重複排除します。3つのコンポーネントが同じキーを同時にリクエストした場合、ネットワークリクエストは1回だけ発行されます。
dedupeオプションはリフレッシュ動作を制御します:
const { data, refresh } = await useFetch('/api/data', {
dedupe: 'cancel' // 新しいリクエストを開始する前に保留中のリクエストをキャンセル
})
Nuxt 4.2以降では、キャンセルサポートが大幅に改善されています。サポートされている場合、高速ナビゲーション中に以前のルートからの古いレスポンスをキャンセルまたは無視でき、古いデータが一時的に表示されるリスクを軽減します。
詳細: https://nuxt.com/docs/api/composables/use-fetch
よくある落とし穴
NuxtのuseFetchを他のライブラリと混同する
NuxtのuseFetchは、@vueuse/coreのuseFetchや類似のユーティリティとは異なります。Nuxtバージョンは、SSRペイロードハイドレーションを自動的に処理します。他のライブラリのuseFetchを使用すると、これを完全にバイパスし、二重フェッチとハイドレーションミスマッチを引き起こします。
useAsyncDataなしでセットアップ内で$fetchを使用する
<script setup>内で$fetchを直接呼び出すと、サーバーとクライアントの両方で実行されます:
// ❌ 2回フェッチされる
const data = await $fetch('/api/users')
// ✅ 1回フェッチされ、正しくハイドレーションされる
const { data } = await useFetch('/api/users')
$fetchはイベントハンドラーとクライアント専用のインタラクションに限定してください。
競合するオプションでキーを再利用する
これは警告とバグを引き起こします:
// ❌ deepオプションが競合
await useAsyncData('users', fetchUsers, { deep: false })
await useAsyncData('users', fetchUsers, { deep: true })
server: falseでハイドレーション前にデータを期待する
server: falseを設定すると、composableをawaitしても、ハイドレーションが完了するまでデータはnullのままです。
まとめ
Nuxt 4のデータフェッチングモデルは、サーバー実行、ペイロードハイドレーション、キーベースのキャッシングを中心としています。キーを安定させ、データソースごとに一意に保ちます。コンポーネント間でキーを共有する場合は、オプションの一貫性を確保してください。SSRセーフなフェッチングにはuseFetchまたはuseAsyncDataを使用し、$fetchはクライアントサイドのインタラクションに限定してください。
これらのパターンをマスターすれば、ほとんどのNuxt開発者を悩ませる二重フェッチとステート共有のバグを回避できます。
よくある質問
これは通常、useFetchやuseAsyncDataの代わりにscript setup内で$fetchを直接使用した場合に発生します。直接の$fetch呼び出しはサーバーとクライアントの両方で実行されます。フェッチをuseFetchまたはuseAsyncDataでラップして、ペイロードハイドレーションを活用してください。これにより、サーバーで1回フェッチし、クライアントでそのデータを再利用します。
URLから直接フェッチする場合はuseFetchを使用してください—URLに基づいてキーを自動的に処理します。カスタムロジック、複数のAPI呼び出しの組み合わせ、またはキャッシュキーの明示的な制御が必要な場合はuseAsyncDataを使用してください。どちらも同じSSRハイドレーションの利点を提供します。
同じキーを共有するコンポーネントは同一のリアクティブステートを共有します。データを独立させるには、各コンポーネントに一意のキーを使用してください。たとえば、2つのコンポーネントが同じエンドポイントをフェッチするが別々のステートが必要な場合は、単にusersではなくusers-sidebarとusers-mainを使用してください。
dedupeオプションは、Nuxtが複数のrefresh呼び出しを処理する方法を制御します。dedupeをcancelに設定すると、新しいリクエストを開始する前に保留中のリクエストを中止します。これにより、ユーザーの高速なインタラクション中の競合状態を回避し、キャンセルがサポートされている場合に新しいレスポンスが優先されることを保証します。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.