
Nuxt 数据获取指南useLazyFetch 非阻塞导航请求完全解析【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxtuseLazyFetch是 Nuxt 官方基于useFetch封装的懒加载数据获取 composable它通过内置开启lazy: true选项让客户端导航在异步请求完成前就立即执行数据在后台继续拉取。本文将从源码实现、API 签名、参数与返回值、加载/错误状态处理、以及真实测试用例五个维度完整讲解在 Nuxt 应用中如何用useLazyFetch构建不阻塞路由切换的 SSR 友好数据流同时剖析它与await、useFetch、useAsyncData之间的边界关系帮助你在正确的场景下做出正确的选择。它解决了什么问题阻塞式导航与懒加载在 Nuxt 中默认的useFetch会通过 Vue 的Suspense机制阻塞客户端导航直到其异步 handler 全部 resolve 之后用户才会真正进入新页面——用户停留在旧页面期间可配合进度条然后直接落在数据已就绪的新页面上。这在用户体验上表现为等待后再进入。而useLazyFetch允许导航立即继续数据在后台获取页面先渲染出加载态loading state数据到达后再更新 UI。useLazyFetch本质就是useFetch设置了lazy: true。官方文档对此的表述是useLazyFetch通过在内部将lazy选项置为true提供了对useFetch的封装使其在 handler 被 resolve 之前就触发导航。在源码层面这一点可以得到精确验证。Nuxt 使用createUseFetch工厂函数生成了useFetch与useLazyFetch两个实例两者共享同一套实现逻辑唯一的差别是工厂默认选项不同。见 fetch.ts 工厂与实例定义export const useFetch: UseFetch (createUseFetch as unknown as { __nuxt_factory: typeof createUseFetch }).__nuxt_factory() export const useLazyFetch: UseFetch (createUseFetch as unknown as { __nuxt_factory: typeof createUseFetch }).__nuxt_factory({ lazy: true, // ts-expect-error private property _functionName: useLazyFetch, }) as ReturnTypetypeof createUseFetch可以看到useLazyFetch就是向createUseFetch工厂传入了{ lazy: true }同时注入内部属性_functionName: useLazyFetch该属性仅用于开发模式下报错信息与诊断提示便于 Nuxt 识别调用来源。需要特别指出的是懒加载在服务器端渲染SSR期间依然会阻塞渲染。lazy只影响客户端导航行为在首屏 SSR 阶段无论是否lazyNuxt 都会在序列化页面 HTML 之前等待请求完成底层通过Suspense与onServerPrefetch实现因此返回给浏览器的首屏 HTML 始终包含完整数据。基本用法一个最小可运行示例与useFetch一样useLazyFetch应在script setup、插件plugin或路由中间件middleware中调用。script setup langts const { status, data: posts } await useLazyFetch(/api/posts) /script template div v-ifstatus pending Loading ... /div div v-else-ifstatus error Error loading posts /div div v-else div v-forpost in posts !-- do something -- /div /div /template关于代码中的await需要给出精确的行为预期这一点即使在官方文档中也常被误解在服务器端await会照常等待请求SSR 渲染会被阻塞直至数据就绪HTML 中始终包含完整数据在客户端导航时对lazy调用执行await会立即 resolve——注意它并不会真的等待请求完成。data在await之后仍处于默认值即undefined或default()工厂的返回值因此你必须通过status在模板中自行处理加载态与错误态。也就是说await与lazy是相互独立的两件事。若你希望在客户端导航时真正等待数据到达再进入页面应当去掉lazy选项即改用useFetch而不是依赖await。这一要点在入门指南数据获取中有专门的警告强调。与useFetch({ lazy: true })、不await三者之间的差别从用户可感知的行为来看以下三种写法看起来相似但底层语义并不相同写法请求发起时机客户端导航SSR 行为useFetch(url)默认已awaitsetup 期间立即发起阻塞导航直到数据就绪等待数据后输出完整 HTMLuseFetch(url, { lazy: true })组件挂载mounted后发起立即导航后台拉取等待数据后输出完整 HTMLuseFetch(url)不awaitsetup 期间立即发起立即导航后台拉取等待数据后输出完整 HTMLuseLazyFetch(url)组件挂载后发起立即导航后台拉取等待数据后输出完整 HTML关键在于不await只是不等待请求仍在 setup 阶段就开始而lazy以及useLazyFetch是把请求真正推迟到组件挂载时再发起意图更明确、语义更自洽。因此官方建议想要非阻塞行为时优先使用useLazyFetch/useLazyAsyncData或在选项中显式写lazy: true。useLazyFetch拥有与useFetch完全一致的签名signature差异仅在于内部已把lazy预设为true。类型签名Signatureexport function useLazyFetchResT, ErrorT NuxtErrorunknown, DataT ResT ( url: string | Request | Refstring | Request | (() string | Request), options?: UseFetchOptionsResT, DataT, ): AsyncDataDataT, ErrorT PromiseAsyncDataDataT, ErrorT需要理解的关键点是返回值AsyncDataDataT, ErrorT PromiseAsyncDataDataT, ErrorT返回对象同时具备 Promise 能力因此你可以await它Promise 会在请求在 SSR 下完成后 resolve直接解构而不awaitdata可能在一段时间内为undefined安全地解构 Promise 的方法then、catch、finally都是可枚举且可解构的该能力由 asyncData.ts 中的Object.defineProperties保证。由于AsyncData是 Promise 与响应式对象融合的特殊类型即使你不写await解构出的status、error、data也始终是 Vue refs可在模板中直接响应式使用而在script setup内访问时需通过.value。参数详解useLazyFetch接受的参数与useFetch完全一致lazy已自动置为trueurlstring | Request | Refstring | Request | () string | Request要请求的 URL 或 Request 对象。支持传入 ref 或返回字符串/Request 的函数从而支持动态端点。options请求配置对象完整定义可参考useFetch的参数说明。所有选项都可以是静态值、ref或 computed 值。useLazyFetch常用且高频的选项包括完整列表见useFetch文档的 Options 表格选项类型默认值作用keyMaybeRefOrGetterstring自动生成用于去重的唯一 key缺省时由 URL、options 与源码调用位置组合哈希生成query/paramsMaybeRefOrGetterSearchParams-追加到 URL 的查询参数对象会被自动序列化serverbooleantrue是否在服务器端发起请求设为false可做纯客户端获取immediatebooleantrue设为false可阻止请求立即发起配合execute()手动触发default() DataT-异步数据到达前data的默认值工厂函数配合useLazyFetch尤其有用transform(input) DataT-对 resolve 后的结果进行转换不可与pick同时使用pickstring[]-只从结果中挑选指定字段可减小 Nuxt payload 体积不可与transform同时使用watchMultiWatchSources \| false-额外 watch 的响应式源数组变化时自动刷新dedupecancel \| defercancel避免同一 key 并发重复请求cancel取消旧请求defer复用进行中的请求enabledMaybeRefOrGetterbooleantrue是否允许请求执行的闸门为false时所有执行都被拦截getCachedData函数见下自定义缓存读取策略返回undefined时触发真实请求为data提供默认值懒加载的黄金搭档由于懒加载下data在初始阶段就是默认值若你想避免模板中的空值判断可以配合default选项。仓库测试用例 use-fetch.test.ts 精确验证了这一行为it(should use default value with lazy, () { const { data, pending } useLazyFetchTestData(/api/test, { default: () ({ method: default, headers: {} }) }) expect(pending.value).toBe(true) expect(data.value).toEqual({ method: default, headers: {} }) expect(data.value).not.toBeNull() expect(data.value.method).toEqual(default) })测试断言了三点请求发出后pending为true、data立即等于default()返回的默认对象而非null/undefined、默认值可被安全访问。getCachedData默认实现const getDefaultCachedData (key, nuxtApp, ctx) nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key]该默认缓存仅当在nuxt.config中开启experimental.payloadExtraction时才生效。返回值AsyncData 对象useLazyFetch返回与useFetch相同的AsyncData对象解构出的成员如下名称类型说明dataRefDataT \| undefined异步请求的结果。懒加载模式下请求完成前为默认值refresh(opts?: AsyncDataExecuteOptions) Promisevoid手动刷新数据。默认情况下 Nuxt 会等待上一次refresh完成才允许下一次执行execute(opts?: AsyncDataExecuteOptions) Promisevoidrefresh的别名errorRefErrorT \| undefined请求失败时的错误对象statusRefidle \| pending \| success \| error请求状态用于区分四种生命周期状态pendingRefboolean请求进行中为true。若开启experimental.pendingWhenIdle则status为idle且无缓存数据时也为trueclear() void重置data为undefined或options.default()的返回值、error为undefined、status置为idle并取消所有进行中的请求四种status状态idle请求尚未开始例如设置了{ immediate: false }或在 SSR 阶段设置了{ server: false }pending请求进行中success请求成功完成error请求失败在实际 SSR 懒加载的首屏场景中idle一般不会被观测到因为 SSR 会先等待请求结束再输出 HTML客户端导航场景下状态流转则为pending → success | error。用watch观测渐进更新的数据因为懒加载下posts一开始可能是undefined你无法在await之后立即访问其内容但可以通过watch观测它的变化。这是文档推荐的标准模式script setup langts /* useLazyFetch lets navigation continue before the fetch completes. * Handle loading and error states in the template. */ const { status, data: posts } await useLazyFetch(/api/posts) watch(posts, (newPosts) { // Because posts might start out null, you wont have access // to its contents immediately, but you can watch it. }) /script template div v-ifstatus pending Loading ... /div div v-else-ifstatus error Error loading posts /div div v-else div v-forpost in posts !-- do something -- /div /div /template实战组合动态 URL、延迟请求与手动刷新useLazyFetch支持响应式 URL 与响应式选项常见于列表筛选、按 ID 查询等动态请求场景。响应式查询参数自动重新请求const id ref(null) const { data, status } useLazyFetch(/api/user, { query: { user_id: id, }, })id变化时 Nuxt 会自动用新查询参数重新请求。该响应式能力的底层实现见 fetch.ts 的_fetchOptions所有 fetch 选项被放入一个reactive对象并作为 watch 源注入useAsyncData的watch数组因此选项一旦变化即触发带cause: watch的自动刷新。computed getter URL复杂路径构建script setup langts const id ref(null) const { data, status } useLazyFetch(() /api/users/${id.value}, { immediate: false, }) /script template div !-- disable the input while fetching -- input v-modelid typenumber :disabledstatus pending div v-ifstatus idle Type a user ID /div div v-else-ifstatus pending Loading ... /div div v-else {{ data }} /div /div /templateimmediate: falseexecute()等待用户交互再取数script setup langts const { data, error, execute, status } await useLazyFetch(/api/comments, { immediate: false, }) /script template div v-ifstatus idle button clickexecute Get data /button /div div v-else-ifstatus pending Loading comments... /div div v-else {{ data }} /div /template上述三个组合场景均来自入门指南数据获取中关于 lazy / computed URL / not-immediate 的完整示例可直接复制运行。底层原理从源码看懒加载如何实现理解useLazyFetch的关键在于lazy: true在useAsyncData中如何改变行为。核心的分支逻辑位于 asyncData.ts 的客户端分支其判断顺序可以概括为SSR 或服务器端import.meta.server且fetchOnServer opts.immediate通过onServerPrefetch(() promise)注册请求Nuxt 会等待该 promise 完成后再序列化页面——因此懒加载不改变 SSR 的 HTML 完整度客户端首屏 / 导航若正在 hydration 且已有服务器数据直接沿用不再发起请求若处于组件 setup 且满足opts.lazy条件则把initialFetch压入_nuxtOnBeforeMountCbs数组推迟到组件onBeforeMount即将挂载时才真正执行请求——这正是不阻塞导航、挂载后再取数的机制来源否则非 lazy立即执行initialFetch()从而阻塞导航。由此可见useFetch与useLazyFetch的分水岭本质上是 asyncData.ts 中这段请求何时发起的分派逻辑——lazy 把请求从导航前 await推迟到组件挂载前注册导航因此不再等待。另一个值得注意的实现细节useAsyncData的execute内建了去重与取消机制。当同一 key 已有进行中的请求时dedupe: cancel默认值会通过AbortController中止旧请求调用clear()同样会触发 abort仓库测试 use-fetch.test.ts 通过 mockAbortController验证了useLazyFetch的clear会取消进行中的/api/sleep请求。与 SSR payload 的关系useLazyFetch作为useFetch家族的成员会自动把响应数据加入 Nuxt payload使数据能够从服务器端传递到客户端避免 hydration 时重复请求。在服务端渲染 hydration 场景下客户端若发现 payload 中已存在该 key 的数据将直接使用而不再发起请求。在获取数据前若要跨组件共享同一份数据可以通过useNuxtData取回用useFetch/useLazyFetch指定相同key创建的 keyed state。若需要自定义默认选项如baseURL或认证头的useFetch变体官方推荐使用createUseFetch——useLazyFetch本身就是该工厂能力的最好例证。重要警告与注意事项围绕useLazyFetch有以下三条必须遵守的边界await不等待数据客户端在客户端导航期间await useLazyFetch(...)会立即 resolvedata仍处于默认值。必须检查模板中的status pending与status error后再使用结果若希望导航真正等待数据请改用useFetch。保留函数名不可自定义useLazyFetch与useFetch一样属于编译器会转换的保留函数名因此你不应把自己的函数命名为useLazyFetch。要创建带预置选项的自定义变体应使用createUseFetch或createUseAsyncData。这在源码中亦有体现_functionName等内部元数据正是由编译期注入用于开发诊断。不要引入来自其他库的同名导入若data解构出来是字符串而非 JSON 解析对象请检查组件中是否意外引入了import { useFetch } from vueuse/core之类的冲突导入。相关资源useFetch API 参考完整的 options 表格、类型定义与返回值说明useAsyncData API 参考更底层的异步数据原语数据获取入门指南lazy、SSR、payload、headers 传递等系统讲解createUseFetch API 参考自定义带默认选项的 useFetch 变体useLazyFetch 源码实现工厂实例定义useAsyncData 核心实现懒加载分派、去重、abort、payload 序列化逻辑仓库单元测试useLazyFetch的默认值、abort 行为等可执行验证【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考