Nuxt 4 的 useAsyncData:SSR 数据请求与缓存实践

从 SSR 首屏、客户端水合、响应式参数、缓存复用到错误处理,系统梳理 Nuxt 4 中 useAsyncData 的正确使用方式与常见陷阱。

Nuxt 4 的 useAsyncData:SSR 数据请求与缓存实践

在 Nuxt 项目里,请求接口并不只是“拿到数据”这么简单。请求可能发生在服务端渲染阶段,也可能发生在客户端导航之后;处理不当时,同一个接口甚至会请求两次。

useAsyncData 会把异步数据放进 Nuxt 的 SSR 生命周期,并将结果写入页面 payload。浏览器水合时可以复用服务端结果,避免重复请求。

useAsyncData、useFetch 和 $fetch 怎么选

方式 适合场景 SSR 数据复用
$fetch 点击提交、删除、刷新等主动操作 不自动处理
useFetch 直接请求一个 HTTP 接口 支持
useAsyncData 组合多个请求、调用业务函数、精细控制缓存 支持

页面初始化数据优先考虑 useFetchuseAsyncData;用户点击按钮后的请求,直接使用 $fetch 通常更清晰。

一个可维护的列表请求

vue
<script setup lang="ts">
const page = ref(1)
const keyword = ref('')

const { data, status, error, refresh } = await useAsyncData(
  'article-list',
  (_nuxtApp, { signal }) => $fetch('/api/articles', {
    params: { page: page.value, keyword: keyword.value || undefined },
    signal,
  }),
  {
    watch: [page, keyword],
    default: () => ({ list: [], total: 0 }),
  },
)
</script>

<template>
  <ArticleSkeleton v-if="status === 'pending'" />
  <ErrorPanel v-else-if="error" @retry="refresh()" />
  <ArticleList v-else :items="data.list" />
</template>

建议保留几个习惯:给请求稳定且语义明确的 key;提供 default;把分页与关键词放进 watch;将 signal 传给 $fetch;同时处理加载、错误和空数据状态。

动态详情页使用响应式 key

vue
<script setup lang="ts">
const route = useRoute()
const slug = computed(() => String(route.params.slug))
const articleKey = computed(() => `article-${slug.value}`)

const { data: article, error } = await useAsyncData(
  articleKey,
  (_nuxtApp, { signal }) => $fetch(`/api/articles/${slug.value}`, { signal }),
)

if (error.value) {
  throw createError({ statusCode: 404, statusMessage: '文章不存在' })
}
</script>

响应式 key 改变时会重新执行请求。从一篇文章跳到另一篇时,不会继续显示旧数据。

不要在 handler 里执行副作用

useAsyncData 的 handler 应尽量只负责读取并返回数据。写日志、发送埋点、弹提示等副作用可能在 SSR 与客户端阶段产生不一致。只执行一次的逻辑可以交给 callOnce;用户触发的写操作放在事件里调用 $fetch

缓存不是永久保存

useAsyncData 主要解决 SSR 数据传递与同 key 状态复用,不等于完整的业务缓存层。是否使用 CDN、Redis 或 Nitro 缓存,仍需根据数据更新频率决定。

多个组件共享同一份数据时,可以使用相同 key 和 useNuxtData

ts
const { data: articleList } = useNuxtData('article-list')

同一个 key 会共享数据状态,因此 handler、transformpickdeep 等关键选项应保持一致。

常见问题检查表

  • handler 没有返回值或返回了 undefined
  • 页面初始化直接使用 $fetch,导致服务端和客户端重复请求。
  • 动态页面使用固定 key,路由切换后数据没有更新。
  • 只写成功状态,没有加载、错误和空状态。
  • 将 token、密钥等私有配置放进 public runtimeConfig。

小结

稳定的数据请求代码通常并不复杂:选对请求方式、明确 key、处理完整状态,并让 handler 保持纯净。先想清楚 SSR 与客户端水合的边界,再谈缓存和性能,Nuxt 的数据流会容易维护很多。

参考:Nuxt 4 useAsyncData 官方文档Nuxt 4 数据请求指南