Загрузка, ошибки и обновление данных
Данные приходят не мгновенно и не всегда: грамотный UI показывает загрузку, обрабатывает ошибку и умеет перезапрашивать.
Суть: useFetch/useAsyncData возвращают не только data, но и status (pending), error и функцию refresh. Опция lazy не блокирует навигацию, watch перезапрашивает при смене параметров, а createError отдаёт корректную страницу ошибки при SSR.
Запрос данных — это всегда три исхода: ещё грузится, успешно пришло, упало с ошибкой. Хороший интерфейс обрабатывает все три. Nuxt даёт для этого готовые реактивные состояния, и грех ими не пользоваться.
Помимо data, композаблы возвращают status (или pending), error и refresh. Базовый паттерн в шаблоне мы уже видели; теперь добавим перезапрос и реактивность параметров:
<script setup>
const page = ref(1)
// watch: при смене page данные перезапросятся автоматически
const { data, status, error, refresh } = await useFetch(
"/api/products",
{ query: { page }, lazy: true }
)
</script>
<template>
<button @click="refresh()">Обновить</button>
<button @click="page++">Следующая страница</button>
</template>
Опция lazy: true говорит «не блокируй навигацию ожиданием» — страница покажется сразу, а данные дольются. Когда реактивный параметр (тут page внутри query) меняется, useFetch сам перезапрашивает — это встроенный watch. А refresh() позволяет перезапросить вручную, например после мутации.
Для ошибок при SSR есть createError: он формирует ошибку с HTTP-статусом, и Nuxt показывает соответствующую страницу ошибки (например, 404 для несуществующего товара) с правильным кодом для поисковика.
<script setup>
const { data } = await useFetch("/api/product/" + id)
if (!data.value) {
throw createError({ statusCode: 404, statusMessage: "Не найдено" })
}
</script>
Есть изящная деталь, которая многих удивляет: когда вы на сервере вызываете $fetch к собственному эндпоинту /api/..., реального сетевого запроса не происходит. Nitro понимает, что обработчик живёт в том же процессе, и вызывает его напрямую — как обычную функцию. Это экономит и время на установление соединения, и ресурсы. На клиенте же тот же $fetch делает настоящий HTTP-запрос. Один и тот же вызов ведёт себя оптимально в обеих средах, и вам не нужно думать об этом — Nuxt берёт оптимизацию на себя.
Как работает под капотом
Статус — это конечный автомат: idle -> pending -> success | error. Композабл переключает его и обновляет реактивные ссылки, заставляя шаблон перерисоваться. refresh сбрасывает статус обратно в pending и заново выполняет загрузку. Реактивные параметры в query/body отслеживаются: их изменение триггерит перезапрос. createError с statusCode при SSR прерывает рендер и отдаёт страницу ошибки с корректным HTTP-кодом.
Смоделируем этот автомат состояний загрузки и перезапрос:
// Конечный автомат состояния запроса.
function createResource(fetcher) {
const state = { status: "idle", data: null, error: null };
async function load() {
state.status = "pending";
try {
state.data = await fetcher();
state.status = "success";
state.error = null;
} catch (e) {
state.status = "error";
state.error = String(e);
}
return { ...state };
}
return { load, get: () => ({ ...state }) };
}
(async () => {
let flaky = 0;
const res = createResource(async () => {
flaky++;
if (flaky === 1) throw new Error("сеть упала"); // первый раз ошибка
return ["товар A", "товар B"];
});
console.log("После 1-й загрузки:", await res.load()); // error
console.log("После refresh: ", await res.load()); // success
})();
Попробуй сам ▶ — первый вызов падает в error, повторный (как refresh) переводит ресурс в success. Так же ведут себя статусы в useFetch.
Частые ошибки
- Игнорировать
error. Необработанная ошибка оставит пользователя с пустым или сломанным экраном. - Не отдавать 404 при SSR. Без
createErrorпоисковик получит код 200 на несуществующую страницу — вред для SEO. - Перезапрашивать всё вручную. Реактивные параметры триггерят перезапрос сами — не дублируйте логику.
Best practices
- Всегда ветвите шаблон по
status/pendingиerror. - Некритичные данные грузите с
lazy: true, чтобы не задерживать показ страницы. - Несуществующие сущности — через
throw createError({ statusCode: 404 })для корректного кода.
Итог: грамотная работа с данными — это не только data, но и состояния загрузки, ошибки и перезапрос. Nuxt даёт их из коробки. Откуда же берутся сами данные? Из серверных маршрутов — переходим к разделу про Nitro.