useAsyncData
useAsyncData 提供了一种在 SSR 友好的组合式函数中访问异步解析数据的方式。
在页面、组件和插件中,你可以使用 useAsyncData 来获取异步解析的数据。
useAsyncData 是一个旨在直接在 Nuxt 上下文 中调用的组合式函数。它返回响应式组合式函数,并处理将响应添加到 Nuxt 的有效载荷(payload)中,以便它们可以在页面水合(hydrate)时从服务端传递到客户端,而无需在客户端重新获取数据。使用
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>
data、status、pending 和 error 是 Vue 的 ref 对象,在 <script setup> 中使用时应通过 .value 访问,而 refresh/execute 和 clear 是普通函数。监听参数 (Watch Params)
内置的 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>
响应式键
你可以使用 computed ref、普通 ref 或 getter 函数作为 key,从而实现当 key 发生变化时自动更新的动态数据获取。
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 原生支持中止信号。
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 函数来手动取消单个请求。
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 来实现自己的中止逻辑。
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)
})
},
)
当以下情况发生时,处理程序的 signal 将被中止:
- 使用
dedupe: 'cancel'发起新请求 - 调用了
clear函数 - 超过了
options.timeout的持续时间
useAsyncData 是一个由编译器转换的保留函数名,因此你不应该将自己的函数命名为 useAsyncData。参数
key:一个唯一的键,用于确保数据获取可以在多个请求之间正确地去重。如果你没有提供 key,将自动为你生成一个基于文件名和行号的唯一键。handler:一个必须返回真值的异步函数(例如,它不能是undefined或null),否则请求可能会在客户端侧重复执行。handler函数应该是无副作用的,以确保在 SSR 和 CSR 水合过程中行为可预测。如果需要触发副作用,请使用callOnce工具。options:server:是否在服务端获取数据(默认为true)lazy:是否在加载路由后解析异步函数,而不是阻塞客户端路由导航(默认为false)immediate:设置为false时,将防止请求立即触发。(默认为true)default:一个工厂函数,用于在异步函数解析前设置data的默认值 —— 配合lazy: true或immediate: false选项使用非常有用。transform:一个函数,可用于在解析后更改handler函数的结果。getCachedData:提供一个返回缓存数据的函数。返回undefined将触发数据获取。默认情况下,它是仅当const getDefaultCachedData = (key, nuxtApp, ctx) => nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key]nuxt.config中的experimental.payloadExtraction启用时才会缓存数据。pick:仅从handler函数的结果中提取此数组中指定的键。watch:监听响应式源以自动刷新。deep:以深层 ref 对象返回数据。默认为false,以浅层 ref 对象返回数据,如果你的数据不需要深度响应,这可以提高性能。dedupe:避免同时多次获取相同的键(默认为cancel)。可选值:cancel- 当发起新请求时取消现有请求。defer- 如果有正在进行的请求,则根本不发起新请求。
timeout- 在超时请求前等待的毫秒数(默认为undefined,即无超时)。
在底层,
lazy: false 使用 <Suspense> 来在获取数据前阻塞路由加载。考虑使用 lazy: true 并实现加载状态,以获得更流畅的用户体验。共享状态和选项一致性
当对多个 useAsyncData 调用使用相同的 key 时,它们将共享相同的 data、error、status 和 pending ref。这确保了组件间的一致性,但要求选项保持一致。
以下选项在所有使用相同 key 的调用中必须保持一致:
handler函数deep选项transform函数pick数组getCachedData函数default值
以下选项可以不同而不会触发警告:
服务器lazyimmediatededupewatch
// ❌ 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 })
使用
useAsyncData 创建的键控状态可以在整个 Nuxt 应用中使用 useNuxtData 获取。返回值
data:传入的异步函数的结果。refresh/execute:可用于刷新handler函数返回数据的函数。error:如果数据获取失败,则包含错误对象。status:指示数据请求状态的字符串。idle:请求尚未开始时,例如:- 当尚未调用
execute且设置了{ immediate: false }时 - 在服务端渲染 HTML 且设置了
{ server: false }时
- 当尚未调用
pending:请求正在进行中。success:请求已成功完成。error:请求已失败。
pending:一个Ref<boolean>,在请求进行中时(即status.value === 'pending')为true。clear:可用于将data设置为undefined(或提供的options.default()值),将error设置为undefined,将status设置为idle,并标记任何当前挂起的请求为已取消。
默认情况下,Nuxt 会等待 refresh 完成后才能再次执行。
如果你没有在服务端获取数据(例如使用
server: false),那么数据不会在水合完成前获取。这意味着即使你在客户端侧 await useAsyncData,data 在 <script setup> 中仍将保持为 undefined。类型
签名
export type AsyncDataHandler<ResT> = (nuxtApp: NuxtApp, options: { signal: AbortSignal }) => Promise<ResT>
export function useAsyncData<DataT, DataE> (
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): AsyncData<DataT, DataE>
export function useAsyncData<DataT, DataE> (
key: MaybeRefOrGetter<string>,
handler: AsyncDataHandler<DataT>,
options?: AsyncDataOptions<DataT>,
): Promise<AsyncData<DataT, DataE>>
type AsyncDataOptions<DataT> = {
server?: boolean
lazy?: boolean
immediate?: boolean
deep?: boolean
dedupe?: 'cancel' | 'defer'
default?: () => DataT | Ref<DataT> | null
transform?: (input: DataT) => DataT | Promise<DataT>
pick?: string[]
watch?: MultiWatchSources | false
getCachedData?: (key: string, nuxtApp: NuxtApp, ctx: AsyncDataRequestContext) => DataT | undefined
timeout?: number
}
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'