Nuxt3中如何实现根据执行上下文自动切换useFetch与$fetch的dynamicFetch工具函数?
Nuxt3中如何实现根据执行上下文自动切换useFetch与$fetch的dynamicFetch工具函数?
我之前也踩过这个一模一样的坑!你遇到的问题核心就是没法准确区分「客户端水合阶段」和「客户端交互阶段」——原来的process.server || nuxtApp.isHydrating判断逻辑,在onMounted这类交互钩子里面会完全失效,因为页面水合完成后isHydrating会变成false,但这时候你其实想要用$fetch而不是useFetch。
先拆解下问题本质:
useFetch是给服务端渲染/客户端水合预取数据用的,它会把数据注入到页面,客户端水合时直接复用,不会重复发起请求;$fetch是给客户端动态请求用的(比如用户点击、onMounted这类后置钩子),属于交互后的按需请求;- 你之前的判断逻辑没区分开「客户端水合完成后的交互场景」,所以在onMounted里调用时会错误触发
useFetch。
那正确的实现思路应该是:
- 服务端环境 → 用
useFetch; - 客户端水合阶段(比如setup里直接await请求)→ 用
useFetch; - 客户端已完成水合(比如onMounted、点击事件)→ 用
$fetch。
下面是我验证过的可行实现代码:
export async function dynamicFetch({ path = "", options = {}, }: { path: string; options?: Record<string, any>; }): Promise<any> { const nuxtApp = useNuxtApp(); // 分离useFetch特有的配置和通用请求配置,避免传递给$fetch时出现不兼容报错 const { server, client, ...baseOptions } = options; const useFetchConfig = { server, client, ...baseOptions }; const fetchConfig = { ...baseOptions }; // 服务端 或 客户端水合阶段:用useFetch做数据预取 if (process.server || nuxtApp.isHydrating) { const { data, error } = await useFetch(path, useFetchConfig); if (error.value) { throw createError({ statusCode: error.value.statusCode ?? 500, statusMessage: error.value.statusMessage ?? "请求失败", }); } return data.value; } // 客户端已完成水合:用$fetch做动态请求 if (process.client) { try { const res = await $fetch.raw(path, fetchConfig); return res._data; } catch (err) { throw createError({ statusCode: (err as any).response?.status ?? 500, statusMessage: (err as any).response?.statusText ?? "请求失败", }); } } // 兜底返回 return null; }
关键细节说明:
- 必须用
useNuxtApp()获取当前的nuxt实例,才能拿到准确的isHydrating状态,不能用其他方式间接获取; - 把
useFetch特有的配置(比如server/client开关)单独分离,避免传递给$fetch时出现配置不兼容的问题; - 错误处理统一用
createError抛出,能和Nuxt的全局错误处理逻辑对齐,比如自动触发错误页面; useFetch返回的是ref响应式对象,所以要取出data.value;$fetch.raw返回的是完整响应对象,取res._data就能拿到业务返回数据。
核心场景测试验证:
- setup里直接调用:
- 服务端:触发
useFetch预取数据,自动注入到页面HTML中; - 客户端水合:触发
useFetch复用服务端数据,不会重复发起请求。
- 服务端:触发
- onMounted里调用:
- 服务端:onMounted本身不会执行,所以无任何请求;
- 客户端:水合已完成,
isHydrating为false,自动触发$fetch发起动态请求,完全符合预期。
- 用户交互触发(比如点击按钮):
- 客户端直接触发
$fetch,属于纯按需的动态请求,不会和预取逻辑冲突。
- 客户端直接触发
额外注意:
这个工具函数是一个Nuxt Composable,所以必须在setup上下文里调用——也就是只能在组件setup、其他Composable、或者Vue生命周期钩子中使用,不能在普通的非Composable函数里调用,否则useNuxtApp()会直接报错。
内容来源于stack exchange
相关产品推荐
相关产品推荐

