Асинхронные действия во Vue.js возникают при работе с HTTP-запросами, таймерами, событиями браузера, динамическим импортом компонентов и асинхронной инициализацией состояния. Архитектура фреймворка позволяет встроить асинхронную логику на уровне компонентов, хранилищ состояния и маршрутизации без нарушения реактивности.
Vue корректно отслеживает изменения, происходящие после завершения асинхронных операций. Главное требование — изменение должно происходить через реактивные источники данных.
import { ref } from 'vue'
const data = ref(null)
const loading = ref(false)
const error = ref(null)
async function loadData() {
loading.value = true
error.value = null
try {
const response = await fetch('/api/items')
data.value = await response.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
ref и reactive сохраняют связь между
асинхронным результатом и шаблоном, даже если данные обновляются спустя
значительное время.
Для загрузки данных при создании компонента используются хуки жизненного цикла.
import { onMounted, ref } from 'vue'
const users = ref([])
onMounted(async () => {
const res = await fetch('/api/users')
users.value = await res.json()
})
export default {
data() {
return {
users: []
}
},
async mounted() {
const res = await fetch('/api/users')
this.users = await res.json()
}
}
Асинхронность в хуках не блокирует отрисовку компонента. Шаблон обновляется по мере поступления данных.
setupsetup может быть объявлена как async. В
этом случае компонент будет ожидать её завершения только при
использовании <Suspense>.
export default {
async setup() {
const res = await fetch('/api/config')
const config = await res.json()
return { config }
}
}
Без <Suspense> асинхронный setup
допустим, но теряется возможность управления состоянием загрузки на
уровне шаблона.
<Suspense>
и управление состоянием загрузки<Suspense> позволяет отложить рендер компонента до
завершения асинхронных зависимостей.
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Загрузка…</div>
</template>
</Suspense>
Компонент внутри default может содержать:
setupdefineAsyncComponent)Динамическая загрузка компонентов снижает размер начального бандла.
import { defineAsyncComponent } from 'vue'
const UserProfile = defineAsyncComponent(() =>
import('./UserProfile.vue')
)
Поддерживаются:
defineAsyncComponent({
loader: () => import('./Dashboard.vue'),
delay: 200,
timeout: 3000
})
Ошибки из async/await не перехватываются автоматически
Vue. Используются стандартные конструкции JavaScript.
try {
await fetchData()
} catch (err) {
console.error(err)
}
Для ошибок рендера применяется errorCaptured или
глобальные обработчики:
app.config.errorHandler = (err) => {
console.error(err)
}
watchwatch поддерживает асинхронные колбэки и отмену
предыдущих вызовов.
watch(query, async (newValue, oldValue, onInvalidate) => {
const controller = new AbortController()
onInvalidate(() => controller.abort())
const res = await fetch(`/api/search?q=${newValue}`, {
signal: controller.signal
})
results.value = await res.json()
})
Такой подход предотвращает гонки запросов при быстром изменении наблюдаемого значения.
Частые асинхронные вызовы оптимизируются через debounce или throttle.
import { ref, watch } from 'vue'
import debounce from 'lodash/debounce'
const term = ref('')
const search = debounce(async (value) => {
await fetch(`/api/search?q=${value}`)
}, 300)
watch(term, (v) => search(v))
Pinia использует асинхронные функции как действия без дополнительных обёрток.
import { defineStore } from 'pinia'
export const useUserStore = defineStore('users', {
state: () => ({
list: [],
loading: false
}),
actions: {
async fetchUsers() {
this.loading = true
this.list = await fetch('/api/users').then(r => r.json())
this.loading = false
}
}
})
Асинхронные действия легко комбинируются, возвращают
Promise и могут вызываться друг из друга.
Vue Router поддерживает асинхронные хуки навигации.
router.beforeEach(async (to, from) => {
const isAuth = await checkAuth()
if (!isAuth && to.meta.requiresAuth) {
return '/login'
}
})
Также маршруты могут содержать асинхронные компоненты:
{
path: '/admin',
component: () => import('./views/Admin.vue')
}
Асинхронные процессы должны корректно завершаться при уничтожении компонента.
import { onUnmounted } from 'vue'
const controller = new AbortController()
fetch('/api/data', { signal: controller.signal })
onUnmounted(() => {
controller.abort()
})
Это особенно важно при работе с запросами, таймерами и подписками.
При SSR асинхронные операции должны завершаться до генерации HTML. Vue автоматически ожидает:
setupРекомендуется выносить загрузку данных в функции, совместимые и с клиентом, и с сервером, избегая прямого доступа к API браузера.
Для крупных приложений асинхронная логика выносится:
export async function fetchPosts() {
const res = await fetch('/api/posts')
return res.json()
}
Такой подход упрощает тестирование, повторное использование и контроль побочных эффектов.
async/await полностью поддерживается на всех
уровнях<Suspense> и асинхронные компоненты дают контроль
над временем рендера