Nuxt 4 的 useAsyncData:SSR 数据请求与缓存实践
在 Nuxt 项目里,请求接口并不只是“拿到数据”这么简单。请求可能发生在服务端渲染阶段,也可能发生在客户端导航之后;处理不当时,同一个接口甚至会请求两次。
useAsyncData 会把异步数据放进 Nuxt 的 SSR 生命周期,并将结果写入页面 payload。浏览器水合时可以复用服务端结果,避免重复请求。
useAsyncData、useFetch 和 $fetch 怎么选
| 方式 | 适合场景 | SSR 数据复用 |
|---|---|---|
$fetch |
点击提交、删除、刷新等主动操作 | 不自动处理 |
useFetch |
直接请求一个 HTTP 接口 | 支持 |
useAsyncData |
组合多个请求、调用业务函数、精细控制缓存 | 支持 |
页面初始化数据优先考虑 useFetch 或 useAsyncData;用户点击按钮后的请求,直接使用 $fetch 通常更清晰。
一个可维护的列表请求
<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
<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:
const { data: articleList } = useNuxtData('article-list')
同一个 key 会共享数据状态,因此 handler、transform、pick、deep 等关键选项应保持一致。
常见问题检查表
- handler 没有返回值或返回了
undefined。 - 页面初始化直接使用
$fetch,导致服务端和客户端重复请求。 - 动态页面使用固定 key,路由切换后数据没有更新。
- 只写成功状态,没有加载、错误和空状态。
- 将 token、密钥等私有配置放进 public runtimeConfig。
小结
稳定的数据请求代码通常并不复杂:选对请求方式、明确 key、处理完整状态,并让 handler 保持纯净。先想清楚 SSR 与客户端水合的边界,再谈缓存和性能,Nuxt 的数据流会容易维护很多。