Олег Марков
Как использовать 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 до уровня Middle — бесплатно!
Nuxt — часть карты развития Frontend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Бесплатные лекции
Все гайды по Nuxt
Лучшие курсы по теме

Nuxt
Антон Ларичев
TypeScript с нуля
Антон Ларичев