useAsyncData
В страницах, компонентах и плагинах можно использовать useAsyncData для доступа к данным, загружаемым асинхронно.
useAsyncData предназначен для вызова в контексте Nuxt. Он возвращает реактивные значения и добавляет ответы в payload Nuxt, чтобы их можно было передать с сервера на клиент без повторной загрузки при гидрации.Использование
<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 не вызывайте в ней await — это может привести к неожиданному поведению. См. рецепт кастомного загрузчика асинхронных данных.data, status, pending и error — это ref-ы Vue, к ним нужно обращаться через .value внутри <script setup>, тогда как refresh/execute и clear — обычные функции.Отслеживание параметров
Встроенная опция watch позволяет автоматически перезапускать функцию загрузки при обнаружении изменений.
<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-функцию — данные будут автоматически обновляться при изменении ключа:
<script setup lang="ts">
const route = useRoute()
const userId = computed(() => `user-${route.params.id}`)
// При смене маршрута и обновлении userId данные будут загружены заново
const { data: user } = useAsyncData(
userId,
() => fetchUserById(route.params.id),
)
</script>
Отмена обработчика (handler)
Обработчик можно сделать отменяемым, используя signal из второго аргумента. Это полезно для отмены запросов при уходе пользователя со страницы. $fetch поддерживает abort signals.
const { data, error } = await useAsyncData(
'users',
(_nuxtApp, { signal }) => $fetch('/api/users', { signal }),
)
refresh() // отменяет текущий $fetch (при dedupe: cancel)
refresh()
clear() // отменяет последний ожидающий обработчик
В refresh/execute можно передать AbortSignal, чтобы вручную отменять отдельные запросы.
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() // отменяет выполняющийся запрос refresh
}
Если ваш handler не поддерживает abort signals, можно реализовать свою логику отмены, используя переданный 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)
})
},
)
Сигнал обработчика отменяется в случаях:
- Выполняется новый запрос при
dedupe: 'cancel' - Вызывается функция
clear - Превышено время
options.timeout
useAsyncData — зарезервированное имя, обрабатываемое компилятором, поэтому не называйте так свою функцию.Параметры
key: уникальный ключ для дедупликации запросов. Если не задан, генерируется по имени файла и строке вызоваuseAsyncData.handler: асинхронная функция, которая должна возвращать значение (неundefinedи неnull), иначе запрос может дублироваться на клиенте.Функцияhandlerдолжна быть без побочных эффектов для предсказуемого поведения при SSR и гидрации. Для побочных эффектов используйте утилитуcallOnce.options:server: выполнять ли загрузку на сервере (по умолчаниюtrue)lazy: разрешать ли асинхронную функцию после перехода по маршруту, не блокируя навигацию (по умолчаниюfalse)immediate: приfalseзапрос не выполняется сразу (по умолчаниюtrue)default: фабрика значения по умолчанию дляdataдо завершения асинхронной функции; полезна приlazy: trueилиimmediate: falsetransform: функция для изменения результатаhandlerпосле загрузкиgetCachedData: функция, возвращающая кэшированные данные. Значениеundefinedзапускает загрузку. По умолчанию:Кэширование работает только при включённой опцииconst getDefaultCachedData = (key, nuxtApp, ctx) => nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key]experimental.payloadExtractionвnuxt.config.pick: выбрать из результатаhandlerтолько указанные в массиве ключиwatch: отслеживать реактивные источники для автообновленияdeep: возвращать данные в виде глубокого ref (по умолчаниюfalse— shallow ref, что может улучшить производительность)dedupe: не выполнять повторный запрос с тем же ключом одновременно (по умолчаниюcancel). Варианты:cancel— отменяет текущие запросы при новомdefer— не создаёт новый запрос, пока есть ожидающий
timeout— таймаут в миллисекундах (по умолчаниюundefined)
lazy: false внутри используется <Suspense>, блокирующий переход до загрузки данных. Для более отзывчивого интерфейса рассмотрите lazy: true и индикатор загрузки.Общее состояние и согласованность опций
При одном и том же ключе в нескольких вызовах useAsyncData они разделяют одни и те же ref-ы data, error, status и pending. Для согласованности между компонентами опции должны совпадать.
Эти опции должны совпадать во всех вызовах с одним ключом:
- функция
handler - опция
deep - функция
transform - массив
pick - функция
getCachedData - значение
default
Эти опции могут отличаться без предупреждений:
serverlazyimmediatededupewatch
// ❌ Вызовет предупреждение в режиме разработки
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 })
// ✅ Допустимо
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, можно получить в приложении с помощью useNuxtData.Возвращаемые значения
data: результат переданной асинхронной функции.refresh/execute: функция для повторной загрузки данных изhandler.error: объект ошибки при неудачной загрузке.status: статус запроса:idle: запрос не начат (например, при{ immediate: false }до вызоваexecuteили при{ server: false }на сервере)pending: запрос выполняетсяsuccess: запрос успешно завершёнerror: запрос завершился с ошибкой
pending:Ref<boolean>, равенtrue, пока выполняется запрос (т.е. покаstatus.value === 'pending').clear: функция сбросаdataвundefined(или вoptions.default()),errorвundefined, установкиstatusвidleи отмены ожидающих запросов.
По умолчанию Nuxt ждёт завершения refresh, прежде чем выполнить его снова.
server: false), они не будут загружены до завершения гидрации. То есть даже при await useAsyncData на клиенте data останется undefined внутри <script setup>.Тип
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'