六维教程

Nuxt4 数据获取进阶

掌握了 useFetch 的基本用法后,这篇学习控制请求时机、延迟加载等进阶选项,以及 Nuxt 4.4 新增的工厂模式,把数据获取做得规范可维护。

常用选项

server(是否在服务端执行)

默认 true,数据在服务端请求。设为 false 则只在客户端请求,适合纯客户端功能:

<script setup>
const { data } = await useFetch('/api/private-data', {
  server: false
})
</script>

设为 false 的请求不会参与 SSR,首屏没有该数据,客户端 hydration 后才请求。

lazy(延迟加载)

默认 false,页面会等待数据准备好才渲染(配合 await)。设为 true 则页面先渲染,数据到了再更新:

<script setup>
const { data, pending } = useFetch('/api/slow-data', {
  lazy: true
})
</script>

<template>
  <!-- pending 期间页面已经渲染,这里显示加载状态 -->
  <div v-if="pending">加载中...</div>
  <div v-else>{{ data }}</div>
</template>

适合慢接口,让页面骨架先出来。useLazyFetchuseLazyAsyncData 是 lazy 的快捷写法,等同于默认 lazy: true

default(默认值)

请求完成前 datanull,用 default 给一个初始值:

<script setup>
const { data } = await useFetch('/api/user', {
  default: () => ({ name: '匿名用户', age: 0 })
})
</script>

<template>
  <p>{{ data.name }}</p>
</template>

配合 lazyimmediate 场景很实用,模板里不用到处判断 null

transform(数据转换)

对请求结果做处理,转换逻辑会应用到双端:

<script setup>
const { data: posts } = await useFetch('/api/posts', {
  // 只保留需要的字段
  transform: (res) => res.map(p => ({ id: p.id, title: p.title }))
})
</script>

immediate(是否立即请求)

设为 false 时初始化不请求,配合 execute 手动触发:

<script setup>
const { data, execute } = useFetch('/api/search', {
  immediate: false,
  query: { keyword: '' }
})

function search() {
  execute()  // 手动发起请求
}
</script>

watch(监听触发)

watch 选项监听指定的响应式值,变化时自动重新请求:

<script setup>
const page = ref(1)

const { data } = await useFetch('/api/posts', {
  query: { page },
  watch: [page]   // page 变化时自动重新请求
})
</script>

提示useFetch 的 options 中传入响应式值(如 query 传 ref)时,默认就会自动监听变化并重新请求,不需要额外写 watch。想要关闭自动监听时显式传 watch: false

刷新数据

refresh 重新执行请求:

<script setup>
const { data, refresh } = await useFetch('/api/users')

function reload() {
  refresh()  // 重新请求
}
</script>

工厂模式 createUseFetch

Nuxt 4.4 引入了 createUseFetchcreateUseAsyncData,可以创建带有自定义默认选项的请求实例。这样项目中所有请求的 baseURL、请求头、错误处理都可以统一管理。

app/composables/ 下创建 useApiFetch

// app/composables/useApiFetch.ts
export const useApiFetch = createUseFetch((options) => {
  const runtimeConfig = useRuntimeConfig()

  return {
    ...options,
    // 统一 baseURL,从运行时配置读取
    baseURL: options.baseURL ?? runtimeConfig.public.apiBase,
    // 统一请求头
    headers: {
      ...options.headers,
      Authorization: 'Bearer token'
    }
  }
})

使用起来和 useFetch 完全一样:

<script setup>
// 自动带上 baseURL 和请求头
const { data } = await useApiFetch('/users')
</script>

也可以创建简单配置的实例:

// app/composables/useClientFetch.ts
// 统一只在客户端请求
export const useClientFetch = createUseFetch({
  server: false
})

工厂模式的好处:

场景 传统写法 工厂模式
统一 baseURL 每个页面写一遍 实例里写一次
统一请求头 每个页面传一遍 headers 实例里写一次
统一关闭 SSR 每个调用传 server: false 实例里写一次
接口路径变化 到处改 URL 只改实例

提示createUseFetch 需要在 composables/ 目录中导出才能生效。函数式写法(传回调)时,回调里可以拿到调用者传入的 options,适合动态计算默认值。

常见坑

解决办法
URL 用模板字符串拼响应式值 改用函数形式 useFetch(() => url)
在条件语句里调用 useFetch 不要条件调用,用 immediate: falseexecute
在事件回调里调用 useFetch 事件里用 $fetch,数据层逻辑留在 setup
忘记 key 导致数据不共享 跨组件共享用 useAsyncData 显式传相同 key

一句话总结:用 serverlazyimmediate 控制请求时机,用 transform 处理数据,用 createUseFetch 统一项目里的请求规范。

上一篇
Nuxt4 数据获取
下一篇
Nuxt4 服务端接口