логотип PurpleSchool
логотип PurpleSchool

Как использовать useAsyncData в Nuxt

Автор

Олег Марков

Введение

В проектах на Nuxt одной из самых популярных задач становится загрузка данных с сервера и последующая интеграция этих данных в страницы вашего приложения. Для этих целей в Nuxt 3 (и в Nuxt Bridge, если вы обновляетесь с Nuxt 2) существует специальный хук useAsyncData. Именно он позволяет удобно, эффективно и правильно с точки зрения SSR/SSG работать с асинхронными данными.

Давайте вместе разберёмся, что такое useAsyncData, как он устроен "под капотом", как его применять на практике в различных ситуациях: от базовой загрузки до кастомизации и оптимизации. Я покажу вам ключевые паттерны, распространённые ошибки и способы их предотвращения. Такие знания помогут уверенно использовать useAsyncData в ваших Nuxt-проектах — независимо от типа рендера (SSR, SSG, SPA).

Что такое useAsyncData в Nuxt

useAsyncData — это компоновочный хук (composable) из Nuxt 3, который позволяет асинхронно получать данные при рендеринге страницы.

Главные особенности useAsyncData

  • Выполняет асинхронный запрос как на сервере при SSR (Server-Side Rendering), так и на клиенте при переходах по страницам.
  • Может быть использован для загрузки данных, необходимых компоненту ещё до отображения DOM.
  • Удобно обрабатывает состояние загрузки, ошибки и позволяет легко обновлять данные.
  • Кэширует результаты данных между сервером и клиентом по умолчанию.

Этот подход помогает избавиться от дублирования логики и упрощает управление асинхронными запросами.

Синтаксис useAsyncData

Посмотрим на базовый синтаксис:

const { data, pending, error, refresh } = await useAsyncData('users', async () => {
  // Получаем пользователей с API
  return $fetch('https://api.example.com/users')
})

Пояснения:

  • Первый аргумент ('users') — уникальный ключ, ассоциируемый с этим запросом (помогает Nuxt кэшировать данные).
  • Второй аргумент — асинхронная функция, возвращающая данные.
  • В возвращаемом объекте:
    • data — реактивная ссылка на ваши данные;
    • pending — булево значение, указывающее, идет ли запрос;
    • error — ошибка, если она возникла;
    • refresh — функция для повторной загрузки данных.

useAsyncData - это composable функция, позволяющая просто и удобно асинхронно получать данные в компонентах Nuxt. Для эффективного использования useAsyncData необходимо понимать, как работают composables, как обрабатывать ошибки и как обеспечить оптимальную производительность при получении данных. Если вы хотите научиться эффективно использовать useAsyncData в Nuxt, приходите на наш большой курс Nuxt - fullstack Vue фреймворк. На курсе 129 уроков и 13 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, решение задач с живым ревью наставника, еженедельные встречи с менторами.

Боевые примеры использования useAsyncData

Пример 1. Простая загрузка данных на сервере

В pages/index.vue:

<script setup>
// Загрузка списка постов при открытии страницы
const { data: posts, pending, error } = await useAsyncData('posts', () => {
  // Simulate API request
  return $fetch('https://jsonplaceholder.typicode.com/posts')
})
</script>

<template>
  <div>
    <div v-if="pending">Загрузка...</div>
    <div v-else-if="error">Ошибка загрузки: {{ error.message }}</div>
    <ul v-else>
      <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
  </div>
</template>

Здесь при первом SSR-запросе данные уже попадают в HTML-ответ, никакой лишней загрузки на клиенте не требуется.

Пример 2. Использование с параметрами (реактивность)

Часто нужно реагировать на изменения параметров запроса. Смотрите пример:

<script setup>
import { ref, watch } from 'vue'

const userId = ref(1)

const { data: user, refresh } = await useAsyncData(
  // Ключ зависит от параметра - для уникальности кэширования
  () => `user-${userId.value}`,
  () => $fetch(`https://jsonplaceholder.typicode.com/users/${userId.value}`)
)

// Обновление данных при изменении userId
watch(userId, () => {
  refresh()
})
</script>

<template>
  <button @click="userId++">Следующий пользователь</button>
  <div v-if="user">{{ user.name }}</div>
</template>

В этом примере пользователь загружается при изменении userId. Благодаря зависимости ключа кэш также ведёт себя корректно.

Пример 3. Загрузка с условием

Допустим, нужно запустить запрос только если выполнено некое условие:

const isEnabled = ref(false)

// useAsyncData не будет ничего делать, пока isEnabled — false
const { data, refresh } = await useAsyncData(
  'conditional',
  () => $fetch('/api/data'),
  { enabled: isEnabled }
)

Параметры и расширенные возможности useAsyncData

Передача опций

В хук можно передавать третий параметр — объект с опциями:

const { data } = await useAsyncData(
  'posts',
  () => $fetch('/api/posts'),
  {
    server: true,       // Запускать на сервере (по умолчанию true)
    lazy: false,        // Дожидаться выполнения при SSR (если true — only on client hydration)
    default: () => [],  // Значение по умолчанию для data
    transform: (data) => data.slice(0, 10), // Обработка результата
    watch: [someReactiveVar] // Рефы, при изменении которых вызывается refresh()
  }
)

Коротко о каждом параметре:

  • server — выполнять ли функцию на сервере (по умолчанию true).
  • lazy — если true, загрузка начнётся только после гидрации клиента.
  • default — функция, возвращающая значение по умолчанию для data до окончания загрузки.
  • transform — функция для постобработки полученных данных.
  • watch — массив реактивных переменных, при изменении которых данные перезапрашиваются.

Пример transform и default

const { data } = await useAsyncData(
  'comments',
  () => $fetch('/api/comments'),
  {
    default: () => [],
    transform: (comments) => comments.filter(comment => comment.approved)
  }
)

Работа с ошибками и состоянием загрузки

Смотрите, как можно показать пользователю разные состояния — загрузка, ошибка, данные готовы:

<script setup>
const { data, pending, error, refresh } = await useAsyncData('notes', () => $fetch('/api/notes'))
</script>

<template>
  <div v-if="pending">Данные подгружаются...</div>
  <div v-else-if="error">Произошла ошибка: {{ error.message }}</div>
  <div v-else>
    <ul>
      <li v-for="note in data" :key="note.id">{{ note.title }}</li>
    </ul>
    <button @click="refresh">Обновить</button>
  </div>
</template>

Кэширование и повторное использование данных

Как работает кэш

Nuxt автоматически кэширует данные, полученные через useAsyncData, чтобы не делать лишние запросы при переходе страниц или навигации. Ключевую роль играет уникальный ключ первого аргумента.

  • Если ключ одинаков и страница уже запрашивалась — данные мгновенно появляются из кэша.
  • После перехода между страницами useAsyncData сохраняет кэшированные данные (если ключ совпал).

Когда кэш сбрасывается?

Кэш сбрасывается при:

  • Изменении значения ключа (например, при изменении route params).
  • Вызове явного refresh().
  • Перезагрузке приложения (страницы).

Принудительное обновление данных

Используйте функцию refresh из возвращаемого объекта:

const { refresh } = await useAsyncData('something', getData)
refresh() // повторяет запрос

SSR, SSG и useAsyncData

Поведение при SSR

  • useAsyncData выполняется на сервере при первом рендере.
  • Данные из асинхронных хуков сериализуются в HTML, чтобы клиент мог их моментально использовать (без повторного запроса).

Поведение при SSG

  • С помощью useAsyncData можно получать данные на этапе генерации страниц (статическая генерация), результат сохраняется в статическом HTML.

SPA-режим

  • useAsyncData работает только на клиенте.

Использование useAsyncData во Vue-компонентах и setup

useAsyncData предназначен для вызова внутри <script setup> (или setup() функции). Не используйте его вне компонента или после инициализации приложения — это нарушает концепцию SSR и кэширования.

Пример верного использования:

<script setup>
const { data } = await useAsyncData('info', fetchInfo)
</script>

Пример неверного использования:

// Нельзя делать так!
const { data } = useAsyncData('outside', fetchSomething)

useAsyncData vs useFetch: различия

Оба хука — useAsyncData и useFetch — предназначены для загрузки асинхронных данных. Но есть различия:

  • useFetch — используется главным образом для запросов HTTP и возвращает немного другой интерфейс (есть status, требует сразу передать URL или опции для fetch). Подходит для простых HTTP-запросов.
  • useAsyncData — более универсален, позволяет работать с любой асинхронной логикой, не обязательно ограничиваясь сетевыми запросами.

Рекомендации по работе с useAsyncData

  • Используйте уникальные ключи. Если два useAsyncData вызова используют один ключ — они разделят кэш между собой, это может привести к неожиданным последствиям.
  • Не используйте await вне setup — это приведёт к синтаксическим ошибкам и неправильному SSR.
  • Следите за глубиной вложенности — хук не должен вызываться слишком глубоко внутри колбэков или watch, используйте его прямо внутри setup.
  • Для сложных реактивных данных лучше использовать опцию watch.
  • Для работы с условными запросами используйте опцию enabled.

Дополнительные возможности

Асинхронные запросы с зависимостями

Часто бывает, что хочется загрузить данные только тогда, когда какая-то переменная уже известна (например, route params):

const route = useRoute()
const { data } = await useAsyncData(
  () => `user-${route.params.id}`,
  () => $fetch('/api/user/' + route.params.id),
  { watch: [() => route.params.id] }
)

Использование useAsyncData совместно с useState

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

const users = useState('users', () => [])
const { data } = await useAsyncData('users', () => $fetch('/api/users'))

// Заполним глобальное состояние, если пусто
watchEffect(() => {
  if (data.value && users.value.length === 0) {
    users.value = data.value
  }
})

Заключение

useAsyncData — основной инструмент загрузки и ведения асинхронных данных в Nuxt 3. Он обеспечивает предсказуемое поведение на сервере и клиенте, кэширует результаты, облегчает обработку состояний загрузки и ошибок, а также гибко конфигурируется под разные сценарии — от статической генерации до динамических пользовательских запросов. Я показал вам, как прокидывать параметры, обновлять данные, обрабатывать ошибки, эффективно работать с кэшем и интегрироваться с внутренним состоянием приложения. Используйте useAsyncData для построения производительных и отзывчивых Nuxt-приложений.

useAsyncData - это один из множества composables, предоставляемых Nuxt. Чтобы создавать гибкие и масштабируемые приложения, необходимо освоить все возможности фреймворка, включая работу с сервером, базами данных и API. На нашем курсе Nuxt - fullstack Vue фреймворк вы найдете все необходимые знания и навыки. В первых 3 модулях уже доступно бесплатное содержание — начните погружаться в Nuxt прямо сегодня.

Частозадаваемые технические вопросы по теме

Как принудительно сбросить кэш useAsyncData при навигации между определенными страницами?

Для этого используйте уникальные ключи, включающие route params или query. Пример:

const route = useRoute()
const { data } = await useAsyncData(
  () => `posts-${route.query.category}`,
  () => $fetch(`/api/posts?category=${route.query.category}`),
  { watch: [() => route.query.category] }
)

Когда query изменится — кэш будет очищен для нового ключа и useAsyncData снова запросит данные.

Как остановить useAsyncData до выполнения запроса (cancel/abort)?

useAsyncData сам по себе не предоставляет abortController для отмены fetch-запроса, но если вы используете $fetch/$axios с поддержкой abort, передайте туда abortController. Например:

const controller = new AbortController()
const { data } = await useAsyncData(
  'delayed',
  () => $fetch('/api/slow', { signal: controller.signal })
)
// Для отмены запроса
controller.abort()

Можно ли вызывать useAsyncData внутри других composable функций?

Да, главное, чтобы этот composable вызывался из setup() и не был вложен в другие функции вне setup. Пример:

export function useUserData(id) {
  return useAsyncData(
    () => `user-${id.value}`,
    () => $fetch('/api/users/' + id.value)
  )
}

Используйте данный composable внутри <script setup>.

Как объединить несколько useAsyncData для параллельной загрузки?

В Nuxt 3 можно вызывать несколько useAsyncData, а затем применить Promise.all для того, чтобы дождаться их завершения, либо обрабатывать каждое состояние отдельно (pending, error и т.д.). Пример:

const { data: posts } = await useAsyncData('posts', fetchPosts)
const { data: users } = await useAsyncData('users', fetchUsers)

Как типизировать useAsyncData в TypeScript?

Передайте тип данных через generic:

const { data } = await useAsyncData<User[]>('users', () => $fetch<User[]>('/api/users'))

Теперь data.value всегда будет иметь правильный тип. На случай если вы используете transform, не забудьте привести его к тому же типу.

Стрелочка влевоГенерация статического сайта с помощью NuxtКак использовать SVG в NuxtСтрелочка вправо

Постройте личный план изучения Nuxt до уровня Middle — бесплатно!

Nuxt — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Nuxt

Генерация статического сайта с помощью NuxtКак использовать useAsyncData в NuxtКак использовать SVG в NuxtКоманды для запуска NuxtРабота с JSON-данными в NuxtКак настроить HTML-шаблон в NuxtГенерация статического сайта с помощью NuxtРуководство по получению данных с Fetch в NuxtКак отключить определенные функции в NuxtРабота с данными в Nuxt с помощью useNuxtDataНастройка и использование cookie в NuxtГайд по аутентификации (auth) в NuxtКак создавать API-маршруты в Nuxt
Использование Vite для ускорения разработки в NuxtРуководство по использованию TypeScript в NuxtКак запустить Nuxt-приложение в productionКак работает Server-Side Rendering SSR в NuxtНастройка и оптимизация серверной части Nuxt-приложенияРуководство по настройке маршрутизации в NuxtКакие проекты разумно реализовывать на NuxtИнструкция по управлению пакетами NPM в NuxtИнтеграция Node.js для Nuxt-приложенияНастройка мета-тегов для SEO в NuxtНастройка и использование HTTPS в NuxtКак отлавливать и обрабатывать ошибки в NuxtКак развернуть Nuxt приложение в DockerРуководство по развертыванию приложений в Nuxt CloudКак оптимизировать сборку на NuxtИнтеграция Laravel и Nuxt
Открыть базу знаний

Лучшие курсы по теме

Иконка ракетыСкоро!
изображение курса

Nuxt

Антон Ларичев
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий