Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Nuxt 4 の Singleton Data Fetching Layer で 何が変わるのか

Avatar for Naoki Haba Naoki Haba
October 25, 2025
1.3k

Nuxt 4 の Singleton Data Fetching Layer で 何が変わるのか

Avatar for Naoki Haba

Naoki Haba

October 25, 2025
Tweet

More Decks by Naoki Haba

Transcript

  1. Nuxt 4 リリースから3 ヶ月 🙋 皆さんはもう Nuxt 4 を使い始めていますか? 今日は、Nuxt

    4 で改善されたデータフェッチの仕組みについてお話しします
  2. Nuxt 3 までの挙動を見てみよう ComponentA.vue ComponentB.vue 同じキー 'user' を使っているが… const {

    data } = useAsyncData('user', async () => { <script setup lang="ts"> console.log(' 🔵 ComponentA: リクエスト開始') const result = await $fetch('/api/users') return result }, { server: false }) </script> const { data } = useAsyncData('user', async () => { <script setup lang="ts"> console.log(' 🟢 ComponentB: リクエスト開始') const result = await $fetch('/api/users') return result }, { server: false }) </script> どうなると思いますか?
  3. 結果:2 回リクエストが実行される 😱 Console 🔵 ComponentA と 🟢 ComponentB の両方でログ

    が出力される Network 同じ key: 'user' なのに 2 回のリクエストが発生
  4. なぜ2 回実行される? 課題: 各コンポーネントで asyncData をコピーし、 refresh も新規作成 → ComponentA

    と ComponentB で それぞれ handler が実行される Nuxt 3 の実装 const asyncData = { ...nuxtApp._asyncData[key] } // オブジェクトをコピー asyncData.refresh = () => handler(nuxtApp) // refresh 関数を新規作成 if (options.immediate) { initialFetch() // ← ここで各コンポーネントごとにフェッチが実行される // 各コンポーネントでの処理 // immediate: true (デフォルト)なら即実行 }
  5. 改善1: 同じkey でref インスタンスを共有 Before (Nuxt 3) After (Nuxt 4)

    users1 !== users2 // 別々の ref // handler が 2 回実行 😢 const { data: users1 } = useAsyncData( 'users', () => $fetch('/api/users') ) const { data: users2 } = useAsyncData( 'users', () => $fetch('/api/users') ) users1 === users2 // 同じ ref // handler は 1 回だけ ✨ const { data: users1 } = useAsyncData( 'users', () => $fetch('/api/users') ) const { data: users2 } = useAsyncData( 'users', () => $fetch('/api/users') )
  6. 実装: _init フラグによる厳密な管理 ポイント _init フラグで 一度だけ初期化 を保証 既に初期化済みの場合は既存のインスタンスを使用 →

    同じ key なら 同じ ref インスタンス を共有 if (!nuxtApp._asyncData[key.value]?._init) { initialFetchOptions.cachedData = options.getCachedData(key.value, nuxtApp, ...); nuxtApp._asyncData[key.value] = createAsyncData(nuxtApp, key.value, _handler, options, ...); } const initialFetch = () => nuxtApp._asyncData[key.value].execute(initialFetchOptions);
  7. 補足 Nuxt 3.17.2 から徐々に実装が進行 Singleton Data Fetching Layer の機能は一度にすべてが導入されたわけで はありません

    Nuxt 3.17.2 から段階的に実装が開始され、Nuxt 4 で完成形となりました この期間に、ref の共有、reactive key のサポート、自動クリーンアップ などが順次追加されました 段階的な実装について
  8. その他の改善点 1. getCachedData の拡張制御 すべてのデータ取得時に呼び出される 初回だけでなく、watch や refresh 時も キャッシュ戦略を柔軟に制御可能

    2. Reactive key のサポート computed や ref を key に使用できる。 Reactive key が変わると自動的にデータを再取得 getCachedData: (key, nuxtApp, ctx) => { // ctx.cause で呼び出し元を判定 // 'initial' | 'watch' | 'refresh:manual' if (ctx.cause === 'initial') { return nuxtApp.payload.data[key] const { data } = useAsyncData('user', () => $fetch('/api/user'), { } return nuxtApp.static.data[key] } }) const userId = ref('1') const key = computed(() => `user-${userId.value}`) const { data } = useAsyncData(key, () => $fetch(`/api/users/${userId.value}`) userId.value = '2' // watch を書く必要がない! // key が変わると自動的にデータを再取得 ) // userId を変更すると自動的に再フェッチ
  9. 1. 自動データクリーンアップ 使われなくなったデータは自動的に削除される asyncData._deps++ if (data?._deps) { data._deps-- // 誰も参照していなければクリーンアップ

    if (data._deps === 0) { data._off() // データを自動削除 } // 参照カウントで追跡 // コンポーネントがアンマウントされたら const unregister = (key) => { const data = nuxtApp._asyncData[key] } } onScopeDispose(() => { unregister(key.value) })
  10. まとめ ✅ 同じ key で ref インスタンスを共有 ✅ Reactive key

    のサポート ✅ getCachedData の拡張制御 ✅ 自動データクリーンアップ ✨ パフォーマンス向上 & メモリ管理の改善 Nuxt 4 Singleton Data Fetching Layer