useAsyncData

源文件
useAsyncData 提供了一种在对 SSR 友好的组合式函数中访问异步解析数据的方法。

在页面、组件和插件中,你可以使用 useAsyncData 来访问异步解析的数据。

useAsyncData 是一个旨在直接在 Nuxt 上下文中调用的组合式函数。它返回响应式组合式函数,并负责将响应添加到 Nuxt 负载中,以便在页面注水时将它们从服务端传递到客户端,而无需在客户端重新获取数据

使用

app/pages/index.vue
<script setup lang="ts">
const { data, status, pending, error, refresh, clear } = await useAsyncData(
  'mountains',
  (_nuxtApp, { signal }) => $fetch('https://api.nuxtjs.dev/mountains', { signal }),
)
</script>
需要带有预定义默认值的自定义 useAsyncData 吗?使用 createUseAsyncData 来创建一个具有完整类型定义的自定义组合式函数。详见自定义 useFetch 指南
你不需要 await useAsyncData。在服务端,无论如何 Nuxt 都会在渲染前等待 Promise 解析,因此返回的 HTML 始终包含数据。await 会影响调用之后发生的事情:带上它时,执行会暂停直到 data 被填充,并且在数据准备好之前会阻塞客户端导航;不带它时,执行会立即继续,在请求解析之前 data 的初始值为其默认值,而在客户端导航时,你需要使用返回的 statuserror ref 自行处理加载和错误状态。这与lazy选项的效果类似,不过 lazy 是启用非阻塞导航的显式方法。
datastatuspendingerror 是 Vue ref。在 <script setup> 中通过 .value 访问它们的值。refresh/executeclear 是普通函数。

监听参数

内置的 watch 选项允许在检测到任何变化时自动重新运行获取函数。

app/pages/index.vue
<script setup lang="ts">
const page = ref(1)
const { data: posts } = await useAsyncData(
  'posts',
  (_nuxtApp, { signal }) => $fetch('https://fakeApi.com/posts', {
    params: {
      page: page.value,
    },
    signal,
  }), {
    watch: [page],
  },
)
</script>

响应式键

你可以使用计算属性 ref、普通 ref 或 getter 函数作为键,从而实现动态数据获取,当键改变时会自动更新

app/pages/[id].vue
<script setup lang="ts">
const route = useRoute()
const userId = computed(() => `user-${route.params.id}`)

// When the route changes and userId updates, the data will be automatically refetched
const { data: user } = useAsyncData(
  userId,
  () => fetchUserById(route.params.id),
)
</script>

让你的 handler 可中止

你可以通过使用第二个参数中提供的 signal 来使你的 handler 函数可中止。这对于在不再需要请求时取消请求非常有用,例如当用户离开页面时。$fetch 原生支持中止信号。

app/pages/index.vue
const { data, error } = await useAsyncData(
  'users',
  (_nuxtApp, { signal }) => $fetch('/api/users', { signal }),
)

refresh() // will actually cancel the $fetch request (if dedupe: cancel)
refresh() // will actually cancel the $fetch request (if dedupe: cancel)
refresh()

clear() // will cancel the latest pending handler

你还可以将 AbortSignal 传递给 refresh/execute 函数以手动取消单个请求。

app/pages/index.vue
const { refresh } = await useAsyncData(
  'users',
  (_nuxtApp, { signal }) => $fetch('/api/users', { signal }),
)
let abortController: AbortController | undefined

function handleUserAction () {
  abortController = new AbortController()
  refresh({ signal: abortController.signal })
}

function handleCancel () {
  abortController?.abort() // aborts the ongoing refresh request
}

如果你的 handler 函数不支持中止信号,你可以使用提供的 signal 实现自己的中止逻辑。

app/pages/index.vue
const { data, error } = await useAsyncData(
  'users',
  (_nuxtApp, { signal }) => {
    return new Promise((resolve, reject) => {
      signal?.addEventListener('abort', () => {
        reject(new Error('Request aborted'))
      })
      return Promise.resolve(callback.call(this, yourHandler)).then(resolve, reject)
    })
  },
)

当发生以下情况时,handler 信号将被中止:

  • 使用 dedupe: 'cancel' 发起新请求时
  • 调用了 clear 函数时
  • 超出了 options.timeout 设置的时长时
useAsyncData 是一个经过编译器转换的保留函数名,因此你不应该将自己的函数命名为 useAsyncData
阅读更多内容,请参见 Docs > 4 X > Getting Started > Data Fetching#useasyncdata

类型

签名
export type AsyncDataHandler<ResT> = (nuxtApp: NuxtApp, options: { signal: AbortSignal }) => Promise<ResT>

export function useAsyncData<ResT, DataE = unknown, DataT = ResT> (
  handler: AsyncDataHandler<ResT>,
  options?: AsyncDataOptions<ResT, DataT>,
): AsyncData<DataT, DataE> & Promise<AsyncData<DataT, DataE>>
export function useAsyncData<ResT, DataE = unknown, DataT = ResT> (
  key: MaybeRefOrGetter<string>,
  handler: AsyncDataHandler<ResT>,
  options?: AsyncDataOptions<ResT, DataT>,
): AsyncData<DataT, DataE> & Promise<AsyncData<DataT, DataE>>

type AsyncDataOptions<ResT, DataT = ResT> = {
  server?: boolean
  lazy?: boolean
  immediate?: boolean
  deep?: boolean
  dedupe?: 'cancel' | 'defer'
  default?: () => DataT | Ref<DataT>
  transform?: (input: ResT) => DataT | Promise<DataT>
  pick?: string[]
  watch?: MultiWatchSources
  getCachedData?: (key: string, nuxtApp: NuxtApp, ctx: AsyncDataRequestContext) => DataT | undefined
  timeout?: number
  enabled?: MaybeRefOrGetter<boolean>
}

type AsyncDataRequestContext = {
  /** The reason for this data request */
  cause: 'initial' | 'refresh:manual' | 'refresh:hook' | 'watch'
}

type AsyncData<DataT, ErrorT> = {
  data: Ref<DataT | undefined>
  refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
  execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
  clear: () => void
  error: Ref<ErrorT | undefined>
  status: Ref<AsyncDataRequestStatus>
  pending: Ref<boolean>
}

interface AsyncDataExecuteOptions {
  dedupe?: 'cancel' | 'defer'
  timeout?: number
  signal?: AbortSignal
}

type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
文档 > 4 X > 入门 > 数据获取中阅读更多信息。

参数

  • key:一个唯一的键,用于确保跨请求正确地对数据获取进行去重。如果你不提供键,将为你自动生成一个对该 useAsyncData 实例的文件名和行号而言唯一的键。
  • handler:一个异步函数,必须返回一个真值(例如,它不能是 undefinednull),否则请求可能会在客户端被重复执行。
    handler 函数应该是无副作用的,以确保在 SSR 和 CSR 注水期间具有可预测的行为。如果需要触发副作用,请使用 callOnce 工具函数。
  • options(对象):异步函数调用的配置。所有选项都可以是静态值、ref 或计算属性值。
选项类型默认描述
服务器booleantrue是否在服务端调用该函数。
lazybooleanfalse如果为 true,则在路由加载后解析(不阻塞导航)。
immediatebooleantrue如果为 false,则阻止立即调用该函数。
默认() => DataT-在异步解析之前,用于生成 data 默认值的工厂函数。
timeout v4.2number-等待调用超时的毫秒数(默认为 undefined,表示无超时)
transform(input: DataT) => DataT | Promise<DataT>-用于在解析后转换结果的函数。
getCachedData v3.8(key, nuxtApp, ctx) => DataT | undefined-返回缓存数据的函数。默认实现见下文。
pickstring[]-仅从结果中挑选指定的键。
watchMultiWatchSources-要监听并自动刷新的响应式数据源数组。
deep v3.8booleanfalse在深层 ref 对象中返回数据。为了提高性能,默认为 false(浅层 ref 对象)。
dedupe v3.9'cancel' | 'defer''cancel'当同时多次触发执行时的策略。
enabled v4.5booleantrue控制 handler 是否可以运行的屏障。当为 false 时,所有执行都会被阻塞(初始获取、execute/refresh 以及 watch 触发),并且从 true 切换到 false 会取消所有进行中的请求,但不会清除 data。重新启用不会自动重新获取数据。
所有选项都可以赋予 computedref 值。系统会监视这些值,如果它们更新,将自动使用新值发起新请求。

getCachedData 默认值

默认的 getCachedData 实现
const getDefaultCachedData = (key, nuxtApp, ctx) => nuxtApp.isHydrating
  ? nuxtApp.payload.data[key]
  : nuxtApp.static.data[key]

只有在启用了 nuxt.config 中的 experimental.payloadExtraction 时,此功能才会缓存数据。

在底层,lazy: false 会使用 <Suspense> 在数据获取之前阻塞路由的加载。考虑使用 lazy: true 并实现加载状态,以获得更流畅的用户体验。
你可以使用 useLazyAsyncData,其行为与在 useAsyncData 中设置 lazy: true 相同。

共享状态与选项一致性

当多个 useAsyncData 调用使用相同的 key 时,它们会共享相同的 dataerrorstatuspending ref。请确保这些调用中的以下选项保持一致。

具有相同 key 的所有调用中,以下选项必须保持一致

  • handler 函数
  • deep 选项
  • transform 函数
  • pick 数组
  • getCachedData 函数
  • default

以下选项可以不同而不会触发警告

  • 服务器
  • lazy
  • immediate
  • dedupe
  • watch
  • enabled
app/pages/index.vue
// ❌ 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 })

// ✅ 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 })
可以使用 useNuxtData 在整个 Nuxt 应用中检索使用 useAsyncData 创建的带键状态。

返回值

此组合式函数返回一个可以被 await 的 Promise,这使得直接在 <script setup> 中使用 data 成为可能(即值将存在,而不是 undefined)。你也可以不等待返回值直接获取这些值,在这种情况下,在获取完成之前,data<script setup> 中可能为 undefined。

即使你不 await 返回值,在 SSR 期间 Nuxt 也会等待请求完成并将解析后的数据发送到客户端。
如果你未在服务端获取数据(例如通过 server: false),则在注水完成之前数据不会被获取。这意味着即使你在客户端 await useAsyncDatadata<script setup> 中仍将保持 undefined
名称类型描述
dataRef<DataT | undefined>传入的异步函数的结果。
refresh(opts?: AsyncDataExecuteOptions) => Promise<void>用于手动刷新数据的函数。默认情况下,Nuxt 会等待上一次 refresh 完成后才能再次执行。
execute(opts?: AsyncDataExecuteOptions) => Promise<void>refresh 的别名。
errorRef<ErrorT | undefined>如果异步函数抛出错误时的错误对象。
statusRef<'idle' | 'pending' | 'success' | 'error'>异步函数调用的状态。用它来区分 idlependingsuccesserror
pendingRef<boolean>当请求正在进行时为 true。启用 experimental.pendingWhenIdle 后,当 statusidle 且没有可用缓存数据时,它也为 true
clear() => voiddata 重置为 undefined(如果提供则为 options.default() 的值),将 error 重置为 undefined,将 status 设置为 idle,并取消任何挂起的调用。
如果你没有 await 返回值,可以安全地解构来自 Promise 的函数(thencatchfinally)。

状态值

  • idle:函数尚未被调用(例如,在服务端渲染时使用 { immediate: false }{ server: false }
  • pending:函数已被调用且 promise 处于 pending 状态
  • success:函数返回了一个值
  • error:函数抛出了一个错误