Олег Марков
Как использовать 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 с нуля
Антон Ларичев