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

Обработка запросов и асинхронных операций в Vue

Автор

Олег Марков

Введение

Во фронтенд-разработке на Vue часто возникает задача работать с удалёнными данными — получать их с сервера, отправлять формы, выполнять различные асинхронные действия. Эта работа требует правильной техники управления асинхронными операциями, обработки состояний загрузки, ошибок и успешного результата.

В этой статье подробно разберём подходы к обработке запросов в приложениях на Vue, определим как реализовать асинхронность с использованием различных API (fetch, axios), рассмотрим подходы к структурированию асинхронного кода как во Vue 2, так и во Vue 3 (Options API, Composition API), и дадим работающие готовые примеры. Особое внимание уделим способам управления состояниями (pending, error, success), обработке ошибок и интеграции асинхронной логики с жизненным циклом компонентов.

Основы асинхронности во Vue

Vue, как и большинство современных фронтенд-фреймворков, работает в однопоточном окружении. Это означает, что любые долгие операции (например, сетевые запросы) должны выполняться асинхронно, чтобы не блокировать пользовательский интерфейс. Для этих целей в JavaScript чаще всего используют промисы и async/await.

Асинхронная функция в компоненте (Options API)

Рассмотрим ситуацию: вам нужно получить список пользователей по API при инициализации компонента. Ниже — пример использования async/await в методе жизненного цикла:

export default {
  data() {
    return {
      users: [],
      isLoading: false,
      error: null,
    }
  },
  async created() {
    // Сигнализируем что загрузка началась
    this.isLoading = true
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/users')
      if (!response.ok) throw new Error('Ошибка сети')
      this.users = await response.json()
    } catch (err) {
      // Обработка ошибки
      this.error = err.message
    } finally {
      // Окончание процесса загрузки
      this.isLoading = false
    }
  }
}

В этом примере:

  • isLoading показывает пользователю, что идёт загрузка данных
  • error хранит текст ошибки, если что-то пошло не так
  • В блоке finally мы обязательно завершаем процесс загрузки, независимо от результата

Здесь мы используем стандартный API браузера fetch, но часто на практике выбирают сторонние библиотеки, такие как axios, для более продвинутой работы с запросами.

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

Иногда загрузка данных происходит не только при создании компонента, но и по взаимодействию пользователя — по клику или другим событиям.

export default {
  data() {
    return {
      user: null,
      isLoading: false,
      error: null
    }
  },
  methods: {
    async fetchUser(id) {
      this.isLoading = true
      this.error = null
      try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
        if (!response.ok) throw new Error('Пользователь не найден')
        this.user = await response.json()
      } catch (e) {
        this.error = e.message
      } finally {
        this.isLoading = false
      }
    }
  }
}

Теперь такой метод можно привязывать к кнопкам или событиям формы.

Использование библиотеки axios

Многие разработчики предпочитают использовать axios из-за простоты работы с JSON, поддержки интерсепторов и отмены запросов.

Установка: npm install axios

Пример работы с axios:

import axios from 'axios'

export default {
  data() {
    return {
      posts: [],
      isLoading: false,
      error: null
    }
  },
  async mounted() {
    this.isLoading = true
    try {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
      // Axios сам преобразует ответ в объект
      this.posts = response.data
    } catch (e) {
      this.error = e.message
    } finally {
      this.isLoading = false
    }
  }
}

Если вам нужен POST-запрос: js await axios.post('/api/data', { name: 'test' })

Асинхронность во Vue 3 с Composition API

Vue 3 предлагает новый способ структурирования кода — Composition API, который позволяет разбивать логику на переиспользуемые функции (composition functions).

Посмотрим на пример асинхронной загрузки с помощью Composition API:

import { ref, onMounted } from 'vue'
import axios from 'axios'

export default {
  setup() {
    // Создаем реактивные переменные
    const users = ref([])
    const isLoading = ref(false)
    const error = ref(null)

    const fetchUsers = async () => {
      isLoading.value = true
      error.value = null
      try {
        const response = await axios.get('https://jsonplaceholder.typicode.com/users')
        users.value = response.data
      } catch (e) {
        error.value = e.message
      } finally {
        isLoading.value = false
      }
    }

    // Загружаем данные при инициализации компонента
    onMounted(fetchUsers)

    // Даем значения и методы наружу
    return {
      users, isLoading, error, fetchUsers
    }
  }
}

Здесь:

  • Используются ref для создания реактивных переменных
  • Логика асинхронного запроса выделена в функцию fetchUsers, которую можно переиспользовать и вызывать по событиям
  • Асинхронная функция вызывается автоматически при монтировании компонента с помощью onMounted

Реиспользуемые Composition Functions

Смотрите, я покажу вам, как выделить асинхронную логику в отдельную функцию, чтобы использовать её в нескольких компонентах.

// useUsers.js
import { ref } from 'vue'
import axios from 'axios'

export function useUsers() {
  const users = ref([])
  const isLoading = ref(false)
  const error = ref(null)

  const load = async () => {
    isLoading.value = true
    error.value = null
    try {
      const res = await axios.get('/api/users')
      users.value = res.data
    } catch (e) {
      error.value = e.message
    } finally {
      isLoading.value = false
    }
  }

  return { users, isLoading, error, load }
}

Теперь в компоненте:

import { onMounted } from 'vue'
import { useUsers } from './useUsers'

export default {
  setup() {
    const { users, isLoading, error, load } = useUsers()
    onMounted(load)
    return { users, isLoading, error, load }
  }
}

Это способствует хорошей организации кода и делает его гораздо проще для тестирования и поддержки.

Управление состоянием загрузки и ошибок

Асинхронные операции почти всегда связаны с тремя состояниями:

  • Ожидание (загрузка)
  • Ошибка
  • Успех

В компонентах удобно отображать различные UI в зависимости от состояния:

<template>
  <div v-if="isLoading">Загружаем...</div>
  <div v-else-if="error">Ошибка: {{ error }}</div>
  <ul v-else>
    <li v-for="user in users" :key="user.id">{{ user.name }}</li>
  </ul>
</template>

Такой подход позволяет быстро понять пользователю, что происходит — и реагировать на ошибки.

Обработка отмены запросов и гонки данных

Иногда запросы нужно отменять, например, когда компоненты демонтируются, чтобы не обновлять состояние "мертвого" компонента. В axios есть специальная поддержка отмены (через CancelToken).

Пример отмены запроса в Composition API:

import { ref, onMounted, onUnmounted } from 'vue'
import axios from 'axios'

export default {
  setup() {
    const users = ref([])
    const isLoading = ref(false)
    const error = ref(null)
    let cancel // Сохраним функцию отмены

    const fetchUsers = async () => {
      isLoading.value = true
      error.value = null
      const source = axios.CancelToken.source()
      cancel = source.cancel
      try {
        const response = await axios.get('https://jsonplaceholder.typicode.com/users', {
          cancelToken: source.token
        })
        users.value = response.data
      } catch (e) {
        if (axios.isCancel(e)) {
          console.log('Запрос отменён')
        } else {
          error.value = e.message
        }
      } finally {
        isLoading.value = false
      }
    }

    onMounted(fetchUsers)

    onUnmounted(() => {
      if (cancel) cancel('Компонент демонтирован')
    })

    return { users, isLoading, error }
  }
}

Теперь, если компонент будет удалён до завершения запроса, мы корректно отменим асинхронную операцию.

Реализация цепочек асинхронных действий

Иногда одна асинхронная операция зависит от результата другой (например, получить токен, а затем использовать его в запросе). Для таких случаев также используйте async/await, чтобы удобно выстраивать цепочку вызовов:

async function fetchDataWithToken() {
  try {
    // Получаем токен
    const tokenResponse = await axios.post('/api/auth', { credentials: '...' })
    const token = tokenResponse.data.token

    // Используем токен для последующего запроса
    const dataResponse = await axios.get('/api/data', {
      headers: { Authorization: `Bearer ${token}` }
    })
    return dataResponse.data
  } catch (e) {
    throw e
  }
}

Добавление глобального управления асинхронными запросами

Для более сложных приложений часто возникает задача централизованно управлять асинхронными состояниями (например, через Vuex или Pinia). Там асинхронные операции реализуются внутри экшенов.

Пример экшена во Vuex:

// store.js
import axios from 'axios'

export default {
  state: () => ({
    posts: [],
    isLoading: false,
    error: null
  }),
  mutations: {
    setLoading(state, payload) { state.isLoading = payload },
    setPosts(state, payload) { state.posts = payload },
    setError(state, payload) { state.error = payload }
  },
  actions: {
    async fetchPosts({ commit }) {
      commit('setLoading', true)
      commit('setError', null)
      try {
        const res = await axios.get('/api/posts')
        commit('setPosts', res.data)
      } catch (e) {
        commit('setError', e.message)
      } finally {
        commit('setLoading', false)
      }
    }
  }
}

Это позволяет реализовать асинхронную логику на уровне состояния всего приложения.

Советы по практическому использованию асинхронности

  • Не забывайте про обработку ошибок: Всегда используйте блок catch для любых асинхронных операций.
  • Используйте состояния загрузки: Это поможет предотвратить многократные запросы и дать пользователю обратную связь.
  • Отменяйте запросы при необходимости: При использовании сторонних библиотек для запросов старайтесь отменять незавершённые запросы при демонтаже компонента.
  • Вынесите общую логику в переиспользуемые функции или store: Это особенно полезно, если один и тот же код используется во многих компонентах.
  • Следите за гонками запросов: Если пользователь быстро переключается между элементами, обработка более старых запросов может некорректно обновить состояние. Используйте флаги или отмену запросов для решения этой проблемы.

Заключение

Работа с асинхронными запросами во Vue требует аккуратной работы со статусами загрузки и ошибок, правильной структуры кода и внимательности к жизненному циклу компонентов. Вы можете использовать как базовые средства JavaScript (fetch, async/await), так и сторонние библиотеки, такие как axios. Разделение логики на переиспользуемые функции (особенно с Composition API во Vue 3) делает ваш код более чистым, гибким и простым для поддержки. Также важно учитывать вопросы отмены запросов при работе с динамическими интерфейсами.


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

Как обработать параллельные запросы в Vue и дождаться, когда все завершатся?

Для такой задачи можно использовать Promise.all. Например:

const [usersRes, postsRes] = await Promise.all([
  axios.get('/api/users'),
  axios.get('/api/posts')
])
// Теперь оба ответа получены, используйте usersRes.data и postsRes.data

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

Как ограничить количество одновременных запросов при массовой загрузке данных?

Создайте очередь или используйте сторонние библиотеки для управления параллелизмом (p-limit, async). Пример ручного ограничения: ```js async function limitedRequests(urls, limit = 3) { const results = [] let idx = 0

async function worker() { while (idx < urls.length) { const i = idx++ results[i] = await fetch(urls[i]) } } await Promise.all(Array(limit).fill(0).map(worker)) return results } ```

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

В axios можно задать таймаут: js axios.get('/api/data', { timeout: 3000 }) // 3 секунды В fetch используйте AbortController для реализации таймаута.

Как организовать повтор неудачного запроса в Vue?

Обновляйте функцию отправки с дополнительной логикой: js async function fetchWithRetry(url, retries = 3) { try { return await axios.get(url) } catch (e) { if (retries > 0) return fetchWithRetry(url, retries - 1) throw e } }

Как передавать токен авторизации во всех запросах без повторения кода?

Настройте глобальный интерцептор в axios: js axios.interceptors.request.use(config => { config.headers.Authorization = 'Bearer ' + localStorage.getItem('token') return config }) Теперь каждый запрос будет автоматически иметь нужный заголовок.

Стрелочка влево3 способа улучшить навигацию Vue с push()Понимание и использование provide inject для передачи данных между компонентамиСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний