useAsyncData

Source
Доступ к асинхронным данным через SSR-совместимый компосабл.

В страницах, компонентах и плагинах можно использовать useAsyncData для доступа к данным, загружаемым асинхронно.

useAsyncData предназначен для вызова в контексте Nuxt. Он возвращает реактивные значения и добавляет ответы в payload 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 в любом случае ждёт разрешения промиса перед рендером, поэтому HTML всегда содержит данные. await влияет на то, что происходит после вызова: с ним выполнение приостанавливается, пока data не заполнится, и клиентская навигация блокируется до готовности данных; без него выполнение продолжается сразу, data начинается со значения по умолчанию до завершения запроса, а при клиентской навигации состояния загрузки и ошибки обрабатываются самостоятельно через ref-ы status и error. Эффект похож на опцию lazy, хотя lazy — явный способ включить неблокирующую навигацию.
data, status, pending и error — это ref-ы Vue, к ним нужно обращаться через .value внутри <script setup>, тогда как refresh/execute и clear — обычные функции.

Отслеживание параметров

Встроенная опция 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-функцию — данные будут автоматически обновляться при изменении ключа:

app/pages/[id].vue
<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.

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

refresh() // отменяет текущий $fetch (при dedupe: cancel)
refresh()
clear() // отменяет последний ожидающий обработчик

В refresh/execute можно передать AbortSignal, чтобы вручную отменять отдельные запросы.

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() // отменяет выполняющийся запрос refresh
}

Если ваш handler не поддерживает abort signals, можно реализовать свою логику отмены, используя переданный 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)
    })
  },
)

Сигнал обработчика отменяется в случаях:

  • Выполняется новый запрос при dedupe: 'cancel'
  • Вызывается функция clear
  • Превышено время options.timeout
useAsyncData — зарезервированное имя, обрабатываемое компилятором, поэтому не называйте так свою функцию.
Узнать больше Docs > 4 X > Getting Started > Data Fetching#useasyncdata.

Параметры

  • key: уникальный ключ для дедупликации запросов. Если не задан, генерируется по имени файла и строке вызова useAsyncData.
  • handler: асинхронная функция, которая должна возвращать значение (не undefined и не null), иначе запрос может дублироваться на клиенте.
    Функция handler должна быть без побочных эффектов для предсказуемого поведения при SSR и гидрации. Для побочных эффектов используйте утилиту 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]
      
      Кэширование работает только при включённой опции experimental.payloadExtraction в nuxt.config.
    • pick: выбрать из результата handler только указанные в массиве ключи
    • watch: отслеживать реактивные источники для автообновления
    • deep: возвращать данные в виде глубокого ref (по умолчанию false — shallow ref, что может улучшить производительность)
    • dedupe: не выполнять повторный запрос с тем же ключом одновременно (по умолчанию cancel). Варианты:
      • cancel — отменяет текущие запросы при новом
      • defer — не создаёт новый запрос, пока есть ожидающий
    • timeout — таймаут в миллисекундах (по умолчанию undefined, то есть без ограничения)
    • enabled v4.5 — барьер, разрешающий или блокирующий запуск handler. При false блокируются все вызовы (начальная загрузка, execute/refresh и срабатывания watch); при переключении truefalse текущий запрос отменяется без очистки data. Повторное включение само по себе не перезапрашивает данные.
Любую опцию можно передать как computed или ref. При изменении значения автоматически выполнится новый запрос.
При lazy: false внутри используется <Suspense>, блокирующий переход до загрузки данных. Для более отзывчивого интерфейса рассмотрите lazy: true и индикатор загрузки.
useLazyAsyncData даёт то же поведение, что и useAsyncData с lazy: true.

Общее состояние и согласованность опций

При одном и том же ключе в нескольких вызовах useAsyncData они разделяют одни и те же ref-ы data, error, status и pending. Для согласованности между компонентами опции должны совпадать.

Эти опции должны совпадать во всех вызовах с одним ключом:

  • функция handler
  • опция deep
  • функция transform
  • массив pick
  • функция getCachedData
  • значение default

Эти опции могут отличаться без предупреждений:

  • server
  • lazy
  • immediate
  • dedupe
  • watch
  • enabled
// ❌ Вызовет предупреждение в режиме разработки
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.

Возвращаемые значения

Композабл возвращает Promise, который можно await: тогда в <script setup> data сразу доступна. Без await значения можно читать напрямую, но data будет undefined до завершения запроса.

Даже без await при SSR Nuxt дождётся завершения запроса и передаст данные на клиент.
Если данные не загружались на сервере (например, при server: false), они не будут загружены до завершения гидрации. То есть даже при await useAsyncData на клиенте data останется undefined внутри <script setup>.
ИмяТипОписание
dataRef<DataT | undefined>Результат переданной асинхронной функции.
refresh(opts?: AsyncDataExecuteOptions) => Promise<void>Повторная загрузка данных. По умолчанию Nuxt ждёт завершения refresh, прежде чем выполнить его снова.
execute(opts?: AsyncDataExecuteOptions) => Promise<void>Псевдоним для refresh.
errorRef<ErrorT | undefined>Объект ошибки, если асинхронная функция выбросила исключение.
statusRef<'idle' | 'pending' | 'success' | 'error'>Статус вызова: idle, pending, success или error.
pendingRef<boolean>true, пока запрос выполняется. С experimental.pendingWhenIdle также true, когда status равен idle и кэшированных данных нет.
clear() => voidСбрасывает data в undefined (или в options.default()), error в undefined, status в idle и отменяет ожидающие вызовы.
Методы Promise (then, catch, finally) можно безопасно деструктурировать, если вы не делали await возвращаемого значения.

Значения status

  • idle: функция ещё не вызывалась (например, { immediate: false } до execute или { server: false } при серверном рендере)
  • pending: функция вызвана, промис ожидает
  • success: функция вернула значение
  • error: функция выбросила ошибку

Тип

Signature
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'
Узнать больше Docs > 4 X > Getting Started > Data Fetching.