Nuxt 数据获取实战指南:useFetch、useAsyncData 与 $fetch 的完整解析
2026/9/6 16:08:38 网站建设 项目流程

Nuxt 数据获取实战指南:useFetch、useAsyncData 与 $fetch 的完整解析

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

Nuxt 通过useFetchuseAsyncData两个 composable 与内置的$fetch工具,在浏览器和服务端两种环境中安全地完成数据获取。本文基于 Nuxt 官方文档 Data Fetching 的完整脉络展开,并结合开源仓库中 fetch.ts、asyncData.ts 等核心源码,讲透三者各自的适用场景、payload 传输机制、全部关键选项以及序列化的底层细节,帮助你在 SSR 应用中做出正确且高效的数据获取决策。

为什么需要useFetchuseAsyncData

Nuxt 是一个支持同构(isomorphic / universal)代码的框架:同一段代码既在服务端执行(渲染 HTML),也在客户端执行(水合 HTML)。如果直接在组件的setup函数里用$fetch发起请求,数据会被拉取两次——服务端渲染一次,客户端水合时再一次。这不仅带来重复请求,还会引发水合不一致、交互可用时间变长等难以预料的问题。

useFetchuseAsyncData解决的核心问题就是:如果 API 调用发生在服务端,数据会通过 payload 转发到客户端,浏览器水合时直接复用,不再重复请求

这个 payload 是一个 JavaScript 对象,可通过useNuxtApp().payload访问(参见 useNuxtApp 文档)。在源码层面,客户端从内联在 HTML 中的__NUXT_DATA__元素中解析它:payload.ts 的getNuxtClientPayload会读取document.getElementById('__NUXT_DATA__')并用parsePayload反序列化。开发时可以用 Nuxt DevTools 的Payload 标签页检查这份数据。

<script setup lang="ts"> const { data } = await useFetch('/api/data') async function handleFormSubmit () { const res = await $fetch('/api/submit', { method: 'POST', body: { // My form data }, }) } </script> <template> <div v-if="data == undefined"> No data </div> <div v-else> <form @submit="handleFormSubmit"> <!-- form input tags --> </form> </div> </template>

在上面的示例中,useFetch确保请求在服务端发起并正确转发给浏览器;而$fetch没有这套机制,更适合仅从浏览器发起的、事件驱动的请求(如表单提交)。

Suspense 与导航阻塞

Nuxt 在底层使用 Vue 的<Suspense>组件,确保在视图所需的所有异步数据就绪之前不会完成页面导航。文档建议可以为页面间导航添加<NuxtLoadingIndicator>进度条,改善等待体验。

关于await的重要说明

文档示例中通常对useFetch/useAsyncData进行await,但这并非总是必须的,理解其确切语义非常关键:

await不会改变服务端渲染的 HTML。服务端渲染时,Nuxt 无论如何都会等待请求完成再序列化页面(底层是<Suspense>onServerPrefetch),所以发给浏览器的永远是填充完整的结果。await真正影响的是你<script setup>中后续代码的执行时机和客户端导航行为:

  • await:执行会暂停直到数据就绪,后续代码可依赖data已填充。客户端导航时,导航会被阻塞,用户停留在当前页(可配合<NuxtLoadingIndicator>),然后落地到数据完整的页面。这是默认行为。
  • 不带await:执行立即继续,请求在后台运行,data先取默认值,请求完成后才填充。客户端导航会立即发生,你需要自己用返回的statuserror处理加载与错误状态。

两种写法没有普适的优劣,选择取决于该路由期望的交互体验。不await的可见效果与lazy选项类似(导航不被阻塞、自行处理加载态),但两者并不相同:lazy是显式标志,会把请求推迟到组件挂载时,而不await只是请求在 setup 期间就开始、只是不阻塞执行。想要非阻塞行为时,推荐显式使用lazy(或useLazyFetch/useLazyAsyncData),让意图更明确。

注意awaitlazy是相互独立的,且在客户端await一个lazy调用不会得到你期望的效果。例如await useLazyFetch(...)await useFetch(..., { lazy: true }):在服务端渲染时照常阻塞,但在客户端导航await会立即 resolve,dataawait之后仍为默认值,你必须通过status处理加载态。若真的希望导航等待数据,应当去掉lazy选项而不是依赖await

$fetch:最底层的网络请求工具

Nuxt 内置了 ofetch 库,并将其全局自动导入为$fetch别名:

<script setup lang="ts"> async function addTodo () { const todo = await $fetch('/api/todos', { method: 'POST', body: { // My todo data }, }) } </script>

注意:仅使用$fetch无法获得请求去重和导航阻塞保护。推荐将其用于客户端交互(事件驱动),或结合useAsyncData获取组件初始数据。

将客户端 Headers 传递给 API

在服务端调用useFetch时,Nuxt 会使用useRequestFetch自动代理客户端的 headers 和 cookies(host等不宜转发的头除外)。

<script setup lang="ts"> const { data } = await useFetch('/api/echo') </script>
// /api/echo.ts export default defineEventHandler(event => parseCookies(event))

这一机制的源码在 ssr.ts 中:createRequestFetch会包装底层fetch,当请求 URL 以/开头时,把event.req.headers中不在UNFORWARDED_HEADERS黑名单里的头逐一注入;而 fetch.ts 第 371 行的关键逻辑是fetchOptions.$fetch || (import.meta.server ? useRequestFetch() : $fetch)——即服务端默认走代理版,客户端走原生$fetchUNFORWARDED_HEADERS黑名单覆盖了acceptcontent-lengthcontent-typehost等 20 余个逐跳(hop-by-hop)或描述请求体的头。

如果你不走useFetch而用同构的$fetch,可以用useRequestHeaders手动取出并转发 cookie:

<script setup lang="ts"> const headers = useRequestHeaders(['cookie']) async function getCurrentUser () { return await $fetch('/api/me', { headers }) } </script>

你也可以直接使用useRequestFetch自动代理 headers。

警告:向外部 API 代理 headers 要极其谨慎,只包含你真正需要的头。以下常见头不应被代理:hostacceptcontent-lengthcontent-md5content-typex-forwarded-hostx-forwarded-portx-forwarded-protocf-connecting-ipcf-ray

useFetch:SSR 安全的请求封装

useFetch内部基于$fetch执行 SSR 安全的网络调用:

<script setup lang="ts"> const { data: count } = await useFetch('/api/count') </script> <template> <p>Page visits: {{ count }}</p> </template>

从源码结构看,fetch.ts 中useFetch的实现最终落到useAsyncData(key, handler, opts):它把 URL、method、query、body 等通过generateOptionSegments参与 key 的哈希生成(第 323 行:'$f' + hashKey([autoKey, url, ...generateOptionSegments(fetchOptions)])),这解释了后文「相同 URL 在不同组件中 key 不同」的缓存语义。useLazyFetch则只是lazy: true的工厂实例(fetch.ts)。

更完整的参数说明见 useFetch API 文档。

useAsyncData:更细粒度的控制

useAsyncData负责包装任意异步逻辑,并在其 resolve 后返回结果。useFetch(url)几乎等价于useAsyncData(url, () => event.$fetch(url)),它就是最常见用法上的开发者体验糖。

useFetch不适用时——例如 CMS 或第三方提供了自己的查询层——就可以用useAsyncData包装自定义调用,同时保留 composable 的全部收益:

<script setup lang="ts"> const { data, error } = await useAsyncData('users', () => myGetFunction('users')) // This is also possible: const { data, error } = await useAsyncData(() => myGetFunction('users')) </script>

第一个参数是唯一的 key,用于缓存第二个参数(查询函数)的响应。直接传查询函数时 key 会自动生成。由于自动生成的 key 只依据useAsyncData的调用位置,建议始终显式创建自己的 key,避免意外行为(比如你自己封装的useAsyncDatacomposable 场景)。设置 key 还有助于通过useNuxtData在组件间共享同一份数据,或刷新特定数据。

<script setup lang="ts"> const { id } = useRoute().params const { data, error } = await useAsyncData(`user:${id}`, () => { return myGetFunction('users', { id }) }) </script>

useAsyncData也很适合包装并等待多个$fetch请求完成后再统一处理结果:

<script setup lang="ts"> const { data: discounts, status } = await useAsyncData('cart-discount', async (_nuxtApp, { signal }) => { const [coupons, offers] = await Promise.all([ $fetch('/cart/coupons', { signal }), $fetch('/cart/offers', { signal }), ]) return { coupons, offers } }) // discounts.value.coupons // discounts.value.offers </script>

注意 handler 的第二参数提供了{ signal },把它传给$fetch可以让请求随组件卸载或刷新而正确取消。从源码看,asyncData.ts 中execute会通过mergeAbortSignals把内部AbortControllerenabled监听器与timeout的信号合并传给 handler。

注意useAsyncData用于获取和缓存数据,不是用来触发副作用(如调用 Pinia action)的——这可能导致空值下的重复执行等意外行为。确需触发副作用时,请使用callOnce工具。

<script setup lang="ts"> const offersStore = useOffersStore() // you can't do this await useAsyncData(() => offersStore.getOffer(route.params.slug)) </script>

详细说明见 useAsyncData API 文档。

返回值

useFetchuseAsyncData的返回值完全一致:

  • data:传入的异步函数的结果;
  • refresh/execute:用于重新执行 handler 刷新数据的函数;
  • clear:把data置为undefined(或options.default()的值)、error置为undefinedstatus置为idle,并标记当前待处理请求为已取消;
  • error:数据获取失败时的错误对象;
  • status:请求状态字符串("idle""pending""success""error")。

dataerrorstatus都是 Vue ref,在<script setup>中可通过.value访问。

源码中这五个字段在 asyncData.ts 的asyncReturn对象中构造,clear通过AbortController.abort取消在途请求后调用内部的clearNuxtDataByKey,把data重置为default()status重置为idle,与文档描述逐条对应。

默认情况下,Nuxt 会等待上一次refresh完成后才允许再次执行。

注意:如果服务端没有获取数据(例如server: false),数据不会在水合完成前被获取。这意味着即使在客户端await useFetchdata<script setup>中仍会是undefined

选项详解

useAsyncDatauseFetch返回相同的对象类型,并接受一组共同的选项(作为最后一个参数),可控制导航阻塞、缓存与执行等行为。以下逐项展开,默认值均对照 asyncData.ts 中opts.xxx ??= 默认值的源码确认。

lazy:不阻塞导航

默认情况下,数据获取 composable 会通过 Vue 的 Suspense 等待异步函数 resolve 后才导航到新页面。lazy选项(默认false)可在客户端导航中忽略这一行为,此时你需要用status手动处理加载态:

<script setup lang="ts"> const { status, data: posts } = useFetch('/api/posts', { lazy: true, }) </script> <template> <!-- you will need to handle a loading state --> <div v-if="status === 'pending'"> Loading ... </div> <div v-else> <div v-for="post in posts"> <!-- do something --> </div> </div> </template>

等价写法是直接使用useLazyFetch/useLazyAsyncData

<script setup lang="ts"> const { status, data: posts } = useLazyFetch('/api/posts') </script>

server: false:仅客户端获取

默认情况下,数据获取 composable 会在客户端和服务端两个环境执行异步函数。把server选项(默认true)设为false可以让调用只发生在客户端。首次加载时,数据要等到水合完成后才获取,因此你必须处理 pending 状态;但后续的客户端侧导航会照常等待数据后再加载页面。

配合lazy使用,对首次渲染不需要的数据(如非 SEO 敏感数据)非常有用:

/* This call is performed before hydration */ const articles = await useFetch('/api/article') /* This call will only be performed on the client */ const { status, data: comments } = useFetch('/api/comments', { lazy: true, server: false, })

useFetch意在setup方法中调用,或直接在生命周期钩子内调用;其他场景应使用$fetch

pick/transform:最小化 payload 体积

pick选项只从 composable 结果中选取指定字段,减少存储在 HTML 文档中的 payload 体积:

<script setup lang="ts"> /* only pick the fields used in your template */ const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'], }) </script> <template> <h1>{{ mountain.title }}</h1> <p>{{ mountain.description }}</p> </template>

需要更多控制或映射多个对象时,可以用transform函数改写查询结果:

const { data: mountains } = await useFetch('/api/mountains', { transform: (mountains) => { return mountains.map(mountain => ({ title: mountain.title, description: mountain.description })) }, })

transformpick不要在同一个调用中混用。源码中 asyncData.ts 的execute完成链是先执行options.transform(_result),再对结果做pick,两者都在数据写入nuxtApp.payload.data[key]之前生效。

注意picktransform不能阻止无用的数据最初被网络请求获取到;它们只是阻止这些数据进入从服务端传到客户端的 payload。

缓存与重新获取

Keys

useFetchuseAsyncData都通过 key 防止重复获取相同数据:

  • useFetch从 URL、fetch 选项和调用位置生成 key。这意味着不同组件中两个相同 URL 的useFetch调用拥有不同的 key,各自发起请求。要在多个组件间共享同一份数据,请在最后一个参数(options 对象)中显式传入相同的key
  • useAsyncData若第一个参数是字符串,则直接作为 key;若第一个参数是查询函数,则按调用位置自动生成 key。

要按 key 读取已缓存的数据,可以用useNuxtData

共享状态与选项一致性

当多个组件用同一个 key 调用useAsyncDatauseFetch时,它们共享同一份dataerrorstatusref。这保证了组件间的一致性,但要求某些选项保持一致。

以下选项在相同 key 的所有调用间必须一致handler函数、deeptransformpickgetCachedDatadefault

// ❌ This will trigger a development warning const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: false }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: true })

以下选项可以安全地不同,不会触发警告:serverlazyimmediatededupewatch

// ✅ This is allowed const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: true }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: false })

开发环境下的一致性校验在 asyncData.ts 中实现:它会对handlertransformpickgetCachedDataserializedefault做哈希比对,并对deep做 ref 深度检查,不一致时通过NUXT_E3004诊断报告警告。

需要相互独立的实例时,使用不同的 key:

// These are completely independent instances const { data: users1 } = useAsyncData('users-1', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) const { data: users2 } = useAsyncData('users-2', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }))
响应式 key

key 可以是 computed ref、普通 ref 或 getter 函数,实现依赖变化时自动更新的动态数据获取:

// Using a computed property as a key const userId = ref('123') const { data: user } = useAsyncData( computed(() => `user-${userId.value}`), () => fetchUser(userId.value), ) // When userId changes, the data will be automatically refetched // and the old data will be cleaned up if no other components use it userId.value = '456'

源码中,asyncData.ts 通过isKeyReactive判断 key 是否响应式,并对响应式 key 挂载一个flush: 'sync'的 watcher:key 变化时先为旧 key 执行unregister(引用计数归零时释放 watcher、中止在途请求),再为新 key 建立容器并触发获取,旧数据在无其他组件使用时被清理。

refreshexecute

要手动获取或刷新数据,使用 composable 提供的executerefresh

<script setup lang="ts"> const { data, error, execute, refresh } = await useFetch('/api/users') </script> <template> <div> <p>{{ data }}</p> <button @click="() => refresh()"> Refresh data </button> </div> </template>

executerefresh的语义化别名,在获取并非立即执行 的场景下更贴切。要全局重新获取或使缓存失效,见clearNuxtDatarefreshNuxtData——两者的实现同样位于 asyncData.ts,refreshNuxtData通过app:data:refresh钩子广播触发各 key 的重新执行。

clear

不需要知道具体 key 时,可以用 composable 返回的clear函数清空数据:

<script setup lang="ts"> const { data, clear } = await useFetch('/api/users') const route = useRoute() watch(() => route.path, (path) => { if (path === '/') { clear() } }) </script>
watch:监听响应式值触发重新获取

watch选项(默认不启用)可以在其他响应式值变化时重跑获取函数,可传入一个或多个可监听源:

<script setup lang="ts"> const id = ref(1) const { data, error, refresh } = await useFetch('/api/users', { /* Changing the id will trigger a refetch */ watch: [id], }) </script>

注意:监听响应式值不会改变被请求的 URL。例如下面代码始终请求最初构造 URL 时的用户 ID,因为 URL 在函数调用时就已经定型:

<script setup lang="ts"> const id = ref(1) const { data, error, refresh } = await useFetch(`/api/users/${id.value}`, { watch: [id], }) </script>

需要根据响应式值改变 URL 时,应使用后文的 computed URL。

另一个细节:提供响应式 fetch 选项(methodbaseURLqueryparamsbodyheaders,见 fetch.ts 中的MAYBE_REF_OR_GETTER_OPTION_KEYS)时,它们会被自动监听并触发重新获取。某些情况下可用watch: false退出该行为:

const id = ref(1) // Won't automatically refetch when id changes const { data, execute } = await useFetch('/api/users', { query: { id }, // id is watched by default watch: false, // disables automatic watching of id }) // doesn't trigger refetch id.value = 2
Computed URL

有时需要根据响应式值计算 URL,并在每次变化时刷新数据。把参数作为响应式值传入query即可,Nuxt 会自动跟踪这些值并在变化时重新获取:

<script setup lang="ts"> const id = ref(null) const { data, status } = useLazyFetch('/api/user', { query: { user_id: id, }, }) </script>

更复杂的 URL 构造可以用返回 URL 字符串的回调作为 computed getter。每次依赖变化,数据都会用新构造的 URL 重新获取;配合immediate: false,可以等到响应式值真正变化后再发起请求:

<script setup lang="ts"> const id = ref(null) const { data, status } = useLazyFetch(() => `/api/users/${id.value}`, { immediate: false, }) </script> <template> <div> <!-- disable the input while fetching --> <input v-model="id" type="number" :disabled="status === 'pending'" > <div v-if="status === 'idle'"> Type a user ID </div> <div v-else-if="status === 'pending'"> Loading ... </div> <div v-else> {{ data }} </div> </div> </template>

若需要在其他响应式值变化时强制刷新,也可以 watch 其他值。

immediate:延迟立即执行

useFetch默认在调用时立即开始获取。设置immediate: false可以阻止这一行为,例如等待用户交互。此时你需要同时使用status处理获取生命周期、execute启动数据获取:

<script setup lang="ts"> const { data, error, execute, status } = await useLazyFetch('/api/comments', { immediate: false, }) </script> <template> <div v-if="status === 'idle'"> <button @click="execute"> Get data </button> </div> <div v-else-if="status === 'pending'"> Loading comments... </div> <div v-else> {{ data }} </div> </template>

status的四个取值含义:

  • idle:获取尚未开始;
  • pending:获取已开始但尚未完成;
  • error:获取失败;
  • success:获取成功完成。

该状态机在源码中的定义是 asyncData.ts 的AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error',初始值为idle,请求发起时置pending,成功后置success,失败(或被用户 abort)时分别置error或回落到idle

此外还有几个值得注意的辅助选项:dedupe默认'cancel'(可改为'defer',同一 key 已有在途请求时直接复用其 promise);timeout以毫秒为单位中止超时请求;enabled支持 ref 或 getter,置false时阻止获取并取消在途请求;deep默认由构建配置决定,设为false可让data以浅 ref 返回以提升性能;serialize控制结果是否写入 payload。

传递 Headers 与 Cookies

在浏览器中调用$fetch时,用户的cookie等 header 会直接发给 API。

通常,出于安全考虑,服务端渲染期间$fetch不包含用户浏览器的 cookies,也不会传递 fetch 响应中的 cookies。但当在服务端相对 URL调用useFetch时,Nuxt 会通过useRequestFetch代理 headers 和 cookies(host等除外),实现细节见上文 ssr.ts 的UNFORWARDED_HEADERScreateRequestFetch

从服务端 API 调用向 SSR 响应传递 Cookies

反方向——把内部请求拿到的 cookies 传回客户端——需要自行处理:

import type { H3Event } from 'h3' export const fetchWithCookie = async (event: H3Event, url: string) => { /* Get the response from the server endpoint */ const res = await $fetch.raw(url) /* Get the cookies from the response */ const cookies = res.headers.getSetCookie() /* Attach each cookie to our incoming Request */ for (const cookie of cookies) { event.res.headers.append('set-cookie', cookie) } /* Return the data of the response */ return res._data }
<script setup lang="ts"> // This composable will automatically pass cookies to the client const event = useRequestEvent() const { data: result } = await useAsyncData(() => fetchWithCookie(event!, '/api/with-cookie')) onMounted(() => console.log(document.cookie)) </script>

Options API 支持

Nuxt 提供了一种在 Options API 中执行asyncData获取的方式:组件定义必须用defineNuxtComponent包裹:

<script> export default defineNuxtComponent({ /* Use the fetchKey option to provide a unique key */ fetchKey: 'hello', async asyncData () { return { hello: await $fetch('/api/hello'), } }, }) </script>

更完整的说明见 defineNuxtComponent 文档。不过<script setup><script setup lang="ts"才是 Nuxt 中声明 Vue 组件的推荐方式。

服务端到客户端的数据序列化

使用useAsyncData/useLazyAsyncData(以及一切利用 Nuxt payload 的机制)把服务端获取的数据传到客户端时,payload 使用devalue进行序列化。这意味着传输的不仅是基础 JSON,还能序列化并 revive 正则、DateMapSetrefreactiveshallowRefshallowReactiveNuxtError等更复杂的数据类型。源码印证:payload.ts 顶部import { parse } from 'devalue'parsePayloaduseNuxtApp()._payloadRevivers作为 revive 回调解析__NUXT_DATA__内容。

你也可以为 Nuxt 不支持的类型定义自己的序列化/反序列化器(definePayloadReducer/definePayloadReviver,同样位于 payload.ts),详见 useNuxtApp 文档。

注意:上述 devalue 机制不适用于用$fetchuseFetch从你的服务端路由获取的数据——那部分走JSON.stringify,见下节。

从 API 路由序列化数据

server目录获取数据时,响应用JSON.stringify序列化。由于序列化只支持 JavaScript 原始类型,Nuxt 会尽力把$fetchuseFetch的返回类型转换成与实际值匹配。

示例

export default defineEventHandler(() => { return new Date() })
<script setup lang="ts"> // Type of `data` is inferred as string even though we returned a Date object const { data } = await useFetch('/api/foo') </script>

自定义序列化函数

在返回对象上定义toJSON即可定制序列化行为;Nuxt 会尊重toJSON的返回类型,不再做类型转换:

export default defineEventHandler(() => { const data = { createdAt: new Date(), toJSON () { return { createdAt: { year: this.createdAt.getFullYear(), month: this.createdAt.getMonth(), day: this.createdAt.getDate(), }, } }, } return data })
<script setup lang="ts"> // Type of `data` is inferred as // { // createdAt: { // year: number // month: number // day: number // } // } const { data } = await useFetch('/api/bar') </script>

使用替代序列化器

Nuxt 目前不支持JSON.stringify之外的替代序列化器,但可以返回普通字符串并结合toJSON保持类型安全。下例以 superjson 为例:

import superjson from 'superjson' export default defineEventHandler(() => { const data = { createdAt: new Date(), // Workaround the type conversion toJSON () { return this }, } // Serialize the output to string, using superjson return superjson.stringify(data) as unknown as typeof data })
<script setup lang="ts"> import superjson from 'superjson' // `date` is inferred as { createdAt: Date } and you can safely use the Date object methods const { data } = await useFetch('/api/superjson', { transform: (value) => { return superjson.parse(value as unknown as string) }, }) </script>

实用配方

通过 POST 请求消费 SSE(Server-Sent Events)

通过 GET 消费 SSE 时可用浏览器原生EventSource或 VueUse 的useEventSource。但通过POST消费 SSE 需要手动处理连接:

// Make a POST request to the SSE endpoint const response = await $fetch<ReadableStream>('/chats/ask-ai', { method: 'POST', body: { query: 'Hello AI, how are you?', }, responseType: 'stream', }) // Create a new ReadableStream from the response with TextDecoderStream to get the data as text const reader = response.pipeThrough(new TextDecoderStream()).getReader() // Read the chunk of data as we get it while (true) { const { value, done } = await reader.read() if (done) { break } console.log('Received:', value) }

并行请求

当多个请求互不依赖时,用Promise.all()并行执行可提升性能:

const { data } = await useAsyncData((_nuxtApp, { signal }) => { return Promise.all([ $fetch('/api/comments/', { signal }), $fetch('/api/author/12', { signal }), ]) }) const comments = computed(() => data.value?.[0]) const author = computed(() => data.value?.[1])

小结

  • $fetch:最简单直接的请求方式,适合事件驱动的客户端交互;无 payload 转发与去重机制。
  • useFetch$fetch的 SSR 安全封装,服务端请求经useRequestFetch自动代理 cookie/headers,数据随 payload 下发客户端,默认阻塞导航直到数据就绪。
  • useAsyncDatauseFetch的底层与超集,可包装任意异步逻辑(第三方 SDK、并行请求组合),通过 key 实现跨组件共享、去重与按 key 刷新/清理。
  • lazyserver: falseimmediate: falsewatch、computed URL 精确控制获取时机;用pick/transform收缩 payload 体积;用status四态(idle/pending/success/error)驱动 UI。
  • 数据经 payload(devalue)与 API 路由(JSON.stringify+toJSON逃生口)两条不同的序列化通道传输,理解差异可避免类型推断与实际值不符的坑。

以上机制均可在仓库源码中逐行验证:useFetch 实现、useAsyncData 实现、headers 代理、payload 解析。

【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询