Skip to content

缓存与刷新

了解 Nuxt 数据获取的缓存机制和刷新策略。掌握缓存,就能避免重复请求、提升页面加载速度;掌握刷新,就能在数据变化时及时更新。

Key 机制——缓存的基础

useFetchuseAsyncData 通过 key 来管理数据缓存。Key 是数据的唯一标识。

ts
// 自动生成 key(useFetch 根据URL + 选项哈希生成)
const { data } = await useFetch('/api/users')
// key 自动为类似 '/api/users::6h2k3' 的哈希值

// 手动指定 key(useAsyncData 必须手动指定)
const { data } = await useAsyncData('my-users', () => $fetch('/api/users'))

// useFetch 也可以手动指定 key
const { data } = await useFetch('/api/users', {
  key: 'my-users',  // 覆盖自动生成的 key
})

Key 的生成规则

  • useFetch:自动基于 URL + 请求选项(method、query、body 等)生成唯一 key
  • useAsyncData:必须手动提供 key
  • 如果两个 useFetch 的 URL 和选项完全相同,它们的 key 也相同,会自动去重

Key 的三大作用

作用说明示例
去重相同 key 的请求只执行一次多个组件请求同一 API
SSR 传递服务端数据通过 key 传递给客户端nuxtApp.payload.data[key]
缓存访问通过 key 获取已缓存的数据useNuxtData(key)

数据共享——多个组件使用相同数据

多个组件使用相同 key 时,只有第一个会发请求,后续的直接使用缓存:

ts
// 组件 A:首次请求
const { data } = await useFetch('/api/config')
// 发起请求,获取数据

// 组件 B(同页面中)
const { data } = useNuxtData('/api/config')
// 直接使用缓存,不再请求

useNuxtData

通过 key 获取已缓存的数据引用,返回 { data, pending, error }。如果 key 对应的数据不存在,dataundefined

典型场景

根组件获取全局配置,子组件通过 useNuxtData 直接使用:

ts
// app.vue — 根组件获取配置
const { data: config } = await useFetch('/api/config', { key: 'app-config' })

// 任意子组件 — 直接使用
const { data: config } = useNuxtData('app-config')

refresh / execute —— 手动刷新数据

ts
const { data, refresh, execute } = await useFetch('/api/users')

// refresh 和 execute 功能完全相同
await refresh()
await execute()

refresh vs execute

两者完全等价,是同一个函数的别名。refresh 语义更明确,executeimmediate: false 场景下更自然。

带选项刷新

ts
// 强制刷新(忽略去重)
await refresh({ dedupe: false })

// 设置超时
await refresh({ timeout: 5000 })

refresh({ dedupe: false })

默认情况下,如果在短时间内多次调用 refresh(),Nuxt 可能会跳过重复请求。dedupe: false 强制每次都发起新请求。

何时需要手动刷新?

场景示例
用户执行了操作后删除文章后刷新列表
用户点击"刷新"按钮手动刷新页面数据
轮询更新数据定时刷新实时数据
表单提交成功后提交评论后刷新评论区
ts
// 删除文章后刷新列表
async function deletePost(id: number) {
  await $fetch(`/api/posts/${id}`, { method: 'DELETE' })
  await refresh()  // 刷新文章列表
}

clearNuxtData —— 清除缓存

清除指定 key 的缓存数据,将 data 重置为默认值:

ts
// 清除单个
clearNuxtData('/api/users')

// 清除多个
clearNuxtData(['users', 'posts'])

// 清除所有
clearNuxtData()

clearNuxtData 的完整行为

  1. data ref 重置为 default 值(有 default 时)或 undefined
  2. error ref 重置为 undefined
  3. status 重置为 'idle'
  4. nuxtApp.payload.data 中删除该 key 的数据

Nuxt 4.4+ 的改进

Nuxt 4.4+ 中,clearNuxtData 会将 data 重置为 default 值而非 undefined

ts
const { data } = await useFetch('/api/count', {
  default: () => 0,
})

clearNuxtData('/api/count')
// Nuxt 4.4+:data 重置为 0(default 值)
// Nuxt 4.3-:data 重置为 undefined

refreshNuxtData —— 刷新指定数据

从任何地方刷新指定 key 的数据(不需要访问组件的 refresh 函数):

ts
// 刷新单个
await refreshNuxtData('/api/users')

// 刷新多个
await refreshNuxtData(['users', 'posts'])

// 刷新所有
await refreshNuxtData()

refreshNuxtData vs refresh

  • refresh():组件内的方法,只能刷新当前组件的数据
  • refreshNuxtData(key):全局方法,可以在任何地方刷新指定 key 的数据

典型场景

在 A 页面修改了数据,需要刷新 B 页面的缓存:

ts
// 在设置页修改了用户信息
async function updateProfile() {
await $fetch('/api/profile', { method: 'PUT', body: formData })
// 刷新其他页面的用户信息缓存
await refreshNuxtData('/api/profile')
}

当服务端修改了 cookie 后,客户端的 useCookie 值可能不同步。refreshCookie 可以手动刷新:

ts
const token = useCookie('auth-token')

// 从浏览器 cookie 中刷新 token 的值
refreshCookie('auth-token')

// 通常在登录/登出后使用
async function logout() {
  await $fetch('/api/logout', { method: 'POST' })
  refreshCookie('auth-token')  // 刷新 cookie 值
  navigateTo('/login')
}

何时需要 refreshCookie

在服务端 API 中通过 setCookiedeleteCookie 修改了 cookie 后,客户端的 useCookie 返回的 ref 值不会自动更新。调用 refreshCookie 可以从浏览器中读取最新值。

useNuxtData —— 访问缓存数据

通过 key 获取已缓存的数据引用:

ts
// 在组件 A 中获取数据
const { data } = await useFetch('/api/config', { key: 'app-config' })

// 在组件 B 中直接访问(不再请求)
const { data: config } = useNuxtData('app-config')

INFO

useNuxtData 返回的是 ref:在 <script> 中要用 .value<template> 中自动解包

如果 key 对应的数据不存在

dataundefined。确保在使用 useNuxtData 之前,已有组件通过 useFetchuseAsyncData 获取了该 key 的数据。

服务端数据传递——SSR 缓存流程

SSR 时,服务端获取的数据通过 Payload 传递给客户端,避免重复请求:

text
1. 服务端执行 useFetch → 获取数据,按 key 缓存到 nuxtApp.payload.data
2. 数据序列化到 HTML 中的 __NUXT__ 对象
3. 客户端 Hydration → 从 Payload 读取数据(不再请求)
4. 后续客户端导航 → 在客户端重新请求

为什么客户端导航需要重新请求?

Payload 数据只在 Hydration(首次加载)时使用。当用户从 A 页面导航到 B 页面时,B 页面的数据需要重新获取。但 B 页面如果之前访问过,可能有 static.data 缓存。

Payload 提取

将 Payload 提取为单独的 JSON 文件,提高缓存效率:

ts
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: true,         // 生成 _payload.json
    // payloadExtraction: 'client',  // v4.4+ 内联 Payload + 生成文件
  },
})

payloadExtraction: 'client'(v4.4+)

ts
export default defineNuxtConfig({
  experimental: {
    payloadExtraction: 'client',
  },
})
模式HTML 中的 Payload_payload.json适用场景
true不内联,引用外部文件✅ 生成纯静态站点
'client'(v4.4+)内联完整 Payload✅ 生成通用(推荐)
false内联❌ 不生成无 CDN 缓存需求

payloadExtraction: 'client' 的优势

  1. 初始 HTML 中内联完整 Payload → 首屏更快(不需要额外请求)
  2. 同时生成 _payload.json → 客户端导航时可以缓存利用
  3. 运行时 LRU 缓存 → 避免重复渲染

缓存策略实战

策略 1:Hydration 时用 Payload,后续用静态缓存

ts
const nuxtApp = useNuxtApp()

const { data } = await useFetch('/api/data', {
  getCachedData(key) {
    if (nuxtApp.isHydrating) {
      // Hydration 时:使用 Payload 数据
      return nuxtApp.payload.data[key]
    }
    // 客户端导航时:使用静态缓存
    return nuxtApp.static.data[key]
  },
})

策略 2:基于时间的缓存

ts
const ONE_HOUR = 60 * 60 * 1000

const { data } = await useFetch('/api/articles', {
  getCachedData(key, nuxtApp) {
    const cached = nuxtApp.static.data[key]

    if (!cached) return undefined

    // 检查缓存是否过期
    const cachedData = nuxtApp.payload.data[key]
    if (cachedData?.timestamp && Date.now() - cachedData.timestamp < ONE_HOUR) {
      return cachedData
    }

    return undefined  // 缓存过期,重新获取
  },
})

策略 3:只在首次加载时请求

ts
const { data } = await useFetch('/api/config', {
  getCachedData(key, nuxtApp) {
    // 只要有缓存就用,永远不重新请求
    return nuxtApp.payload.data[key] || nuxtApp.static.data[key]
  },
})

INFO

getCachedData 返回 undefined 才会重新请求 如果返回了任何值(包括 null),都不会重新请求

封装缓存组合式函数

ts
// app/composables/useCachedFetch.ts
export function useCachedFetch<T>(url: string, options: Record<string, any> = {}) {
  const nuxtApp = useNuxtApp()

  return useFetch<T>(url, {
    ...options,
    key: options.key || url,
    getCachedData(key) {
      if (nuxtApp.isHydrating) {
        // Hydration 时使用 Payload 数据
        return nuxtApp.payload.data[key]
      }

      // 客户端导航时使用静态缓存
      return nuxtApp.static.data[key]
    },
  })
}

使用:

ts
const { data } = await useCachedFetch('/api/articles')

完整示例——带缓存的列表页

vue
<script setup>
const page = ref(1)
const nuxtApp = useNuxtApp()

const { data, pending, refresh } = await useFetch('/api/articles', {
  query: { page },
  watch: [page],
  key: computed(() => `articles-page-${page.value}`),
  default: () => ({ items: [], total: 0 }),
  getCachedData(key) {
    if (nuxtApp.isHydrating) {
      return nuxtApp.payload.data[key]
    }
    return nuxtApp.static.data[key]
  },
})

// 删除文章后刷新当前页
async function deleteArticle(id: number) {
  await $fetch(`/api/articles/${id}`, { method: 'DELETE' })
  await refresh()
}
</script>

<template>
  <div v-if="pending">加载中...</div>
  <div v-else>
    <ArticleCard v-for="article in data.items" :key="article.id" :article="article" />
    <Pagination v-model="page" :total="data.total" />
  </div>
</template>

知识脉络

text
useFetch → useAsyncData → $fetch → 懒加载获取 → 数据获取选项

  └─→ 你在这里:缓存与刷新

        ├─→ 下一步:SSR 数据传递(Payload 详解)

        └─→ 相关:useFetch(key / dedupe 选项)

基于 Nuxt 4 官方文档整理编写