Vue

Загрузка данных

Список заказов грузится прямо в компоненте: fetch в onMounted, результат в ref, и всё работает ровно до первого реального сценария. Запрос упал, а спиннер крутится вечно. Пользователь переключил вкладку до ответа, и устаревший ответ затёр свежий. Логику фетча скопировали в три компонента, и теперь правка заголовков идёт в трёх местах. Загрузка данных это не одна строка fetch, а связка loading, error, данные и refetch, которую выносят в композабл и переиспользуют.

  • Список заказов: useOrders() инкапсулирует загрузку, loading-спиннер и кнопку повтора при ошибке
  • Профиль пользователя: axios-инстанс с базовым URL и токеном в одном месте, а не в каждом компоненте
  • Поиск с фильтрами: при смене параметров запрос перезапускается, старый ответ отбрасывается
  • Дашборд: несколько композаблов грузят виджеты независимо, каждый со своим loading и error
  • Лента: пагинация через refetch с новым курсором без дублирования логики запроса

Предварительные знания

  • Реактивность: ref для хранения данных и состояния загрузки
  • watch и watchEffect для реакции на смену параметров
  • Композаблы: функция use*, возвращающая реактивное состояние и методы

Фетч внутри композабла

Запрос данных, написанный прямо в компоненте, привязывает логику к одному месту и копируется при повторном использовании. Композабл use* выносит фетч в переиспользуемую функцию, которая возвращает реактивное состояние. Компонент становится тонким: вызвал композабл, отрисовал результат.

fetch не считает HTTP-статусы 4xx и 5xx ошибкой: промис резолвится, и нужно вручную проверять response.ok. Без этой проверки сервер вернул 500, а код посчитал запрос успешным и попытался распарсить тело ошибки как данные.

Композабл сам не решает когда грузить. Он отдаёт refetch, а вызов в onMounted, по клику или при смене параметра остаётся за компонентом. Так один композабл подходит и для авто-загрузки, и для загрузки по действию.

Зачем загрузку данных выносят в композабл use* вместо запроса прямо в компоненте?

Состояние loading, error и refetch

Полезный композабл фетча отдаёт четыре вещи: data, loading, error и refetch. loading управляет спиннером и блокировкой кнопок, error даёт сообщение и кнопку повтора, refetch перезапускает запрос. Этот контракт делает обработку каждого состояния в шаблоне явной.

Состояниеloadingerrordata
Идёт запросtruenullпрежнее или пустое
Успехfalsenullответ сервера
Ошибкаfalseобъект Errorпрежнее

error обнуляют в начале каждого запроса, а loading выставляют в false в блоке finally. Иначе после неудачного запроса error останется висеть при следующей удачной загрузке, а упавший запрос навсегда оставит спиннер крутиться.

Почему loading сбрасывают в false именно в блоке finally?

fetch против axios

fetch встроен в браузер, не требует зависимостей и достаточен для простых случаев, но ручной работы больше: проверка response.ok, явный вызов res.json(), отдельная настройка заголовков на каждый запрос. axios это библиотека с перехватчиками, базовым URL, авто-парсингом JSON и тем, что 4xx/5xx уже считаются ошибкой и попадают в catch.

Критерийfetchaxios
ЗависимостьВстроен в браузерВнешний пакет
4xx/5xx как ошибкаНет, проверяют response.okДа, попадает в catch
JSONres.json() вручнуюПарсится автоматически
ПерехватчикиНетRequest/response interceptors
Базовый URL и таймаутВручнуюЧерез axios.create

Выбор не идеологический. Для пары запросов без общих заголовков fetch экономит зависимость. Когда нужны единый базовый URL, токен в каждом запросе, единая обработка 401 и таймауты, axios-инстанс с перехватчиками убирает копипасту.

Чем поведение fetch отличается от axios при HTTP-статусе 500?

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

Логика запроса живёт в композабле, вызов запускает компонент в нужный момент: onMounted для авто-загрузки, по клику для действия, через watch при смене параметров. Когда параметры меняются часто (поиск, фильтры), возникает гонка: ответ на старый запрос приходит позже ответа на новый и затирает свежие данные. Это решают отменой устаревшего запроса через AbortController.

AbortError это ожидаемое прерывание, а не настоящая ошибка. Его отличают по name === 'AbortError' и проглатывают, иначе отмена устаревшего запроса покажет пользователю фальшивое сообщение об ошибке.

Как предотвращают гонку, когда параметры запроса меняются быстрее, чем приходят ответы?

Связь с другими темами

Урок про ручную загрузку данных. Дальше курс даёт декларативные и готовые инструменты:

  • Suspense и асинхронные компоненты — Декларативная граница загрузки вместо ручного отслеживания loading
  • VueUse — useFetch как готовая реализация того же паттерна loading/error/refetch

Итог

  • Загрузку данных выносят в композабл use*, чтобы переиспользовать запрос с его состоянием в разных компонентах
  • Минимальный контракт композабла: data, error, loading и функция refetch для повторного запроса
  • fetch встроен в браузер и требует ручной проверки response.ok; axios даёт перехватчики, базовый URL и авто-JSON
  • Гонки запросов решают через AbortController или проверку актуальности: устаревший ответ не должен затирать свежий
  • Компонент остаётся тонким: он вызывает композабл и рисует состояние, а вся логика запроса живёт в композабле

Связанные уроки

  • vue-31-suspense-async — Suspense даёт декларативную обработку асинхронной загрузки на уровне границы вместо ручного loading-флага
  • vue-34-vueuse — VueUse предлагает готовый useFetch, который инкапсулирует тот же паттерн loading/error/refetch
Загрузка данных

0

1

Войти