16. Обработка ошибок
🚨 Обработка ошибок в Nuxt 3
Заголовок раздела «🚨 Обработка ошибок в Nuxt 3»Nuxt предоставляет комплексную систему обработки ошибок как на клиенте, так и на сервере. Понимание этой системы критически важно для создания надёжных приложений.
📄 Файл error.vue
Заголовок раздела «📄 Файл error.vue»error.vue — глобальная страница ошибок Nuxt. Она показывается вместо всего приложения при фатальных ошибках:
<script setup lang="ts">import type { NuxtError } from '#app'
const props = defineProps<{ error: NuxtError}>()
const handleError = () => clearError({ redirect: '/' })
const errorMessages = { 404: 'Страница не найдена', 403: 'Доступ запрещён', 500: 'Внутренняя ошибка сервера', 503: 'Сервис недоступен'}
const message = computed(() => errorMessages[props.error.statusCode] || props.error.message)</script>
<template> <div class="error-page"> <div class="error-code">{{ error.statusCode }}</div> <h1>{{ message }}</h1> <p v-if="error.statusCode !== 404">{{ error.message }}</p> <button @click="handleError"> Вернуться на главную </button> </div></template>🔍 useError()
Заголовок раздела «🔍 useError()»useError() возвращает реактивное состояние текущей ошибки:
<script setup lang="ts">const error = useError()
// Проверка наличия ошибкиif (error.value) { console.log('Код ошибки:', error.value.statusCode) console.log('Сообщение:', error.value.message) console.log('Стек:', error.value.stack)}</script>
<template> <div v-if="error"> Ошибка: {{ error.statusCode }} </div></template>🔨 createError()
Заголовок раздела «🔨 createError()»createError() создаёт ошибку с полным контролем над её свойствами:
// В API-маршруте (server/api/users/[id].ts)export default defineEventHandler(async (event) => { const id = getRouterParam(event, 'id') const user = await findUser(id)
if (!user) { throw createError({ statusCode: 404, statusMessage: 'Not Found', message: `Пользователь с ID ${id} не найден`, data: { userId: id } }) }
return user})<!-- В компоненте страницы --><script setup lang="ts">const { data, error } = await useFetch('/api/users/123')
if (error.value) { throw createError({ statusCode: error.value.statusCode, message: error.value.message, fatal: true // делает ошибку фатальной — показывает error.vue })}</script>📢 showError()
Заголовок раздела «📢 showError()»showError() программно показывает страницу ошибки:
// В composableexport function useRequireAuth() { const user = useUser()
if (!user.value) { showError({ statusCode: 401, message: 'Необходима авторизация' }) }}<script setup lang="ts">const handleDelete = async (id: string) => { try { await $fetch(`/api/items/${id}`, { method: 'DELETE' }) } catch (err) { // Показать страницу ошибки showError({ statusCode: err.statusCode || 500, message: 'Не удалось удалить элемент' }) }}</script>🧹 clearError()
Заголовок раздела «🧹 clearError()»clearError() сбрасывает ошибку и опционально выполняет редирект:
// Сброс без редиректаawait clearError()
// Сброс с редиректом на главнуюawait clearError({ redirect: '/' })
// В компонентеconst handleRetry = async () => { await clearError({ redirect: '/dashboard' })}<template> <div> <h1>{{ error.statusCode }}</h1> <div class="actions"> <button @click="clearError()">Попробовать снова</button> <button @click="clearError({ redirect: '/' })">На главную</button> </div> </div></template>🪝 onErrorCaptured
Заголовок раздела «🪝 onErrorCaptured»Vue-хук для перехвата ошибок из дочерних компонентов:
<script setup lang="ts">onErrorCaptured((err, instance, info) => { console.error('Перехвачена ошибка:', err) console.log('Компонент:', instance?.$options.name) console.log('Откуда:', info)
// Отправка в Sentry $sentry.captureException(err, { extra: { component: instance?.$options.name, info } })
// Вернуть false — предотвратить дальнейшее распространение return false})</script>🌐 Ошибки сервера vs клиента
Заголовок раздела «🌐 Ошибки сервера vs клиента»Поведение ошибок отличается в зависимости от контекста:
Серверные ошибки (SSR)
Заголовок раздела «Серверные ошибки (SSR)»export default defineEventHandler(async (event) => { // Ошибка на сервере throw createError({ statusCode: 500, message: 'Ошибка базы данных' }) // → Показывает error.vue с серверной стороны})<!-- pages/data.vue — ошибка во время SSR --><script setup lang="ts">const { data } = await useFetch('/api/data')// Если useFetch бросает fatal error → error.vue во время SSR</script>Клиентские ошибки
Заголовок раздела «Клиентские ошибки»<script setup lang="ts">// Ошибка при взаимодействии пользователяconst handleAction = async () => { try { await riskyOperation() } catch (err) { // Мягкая ошибка — показываем в UI errorMessage.value = err.message // или // Жёсткая ошибка — показываем error.vue showError({ statusCode: 500, message: err.message }) }}</script>📄 Кастомная страница 404
Заголовок раздела «📄 Кастомная страница 404»Для кастомного 404 создайте файл error.vue с обработкой кода 404:
<script setup lang="ts">const props = defineProps<{ error: NuxtError }>()
const is404 = computed(() => props.error.statusCode === 404)</script>
<template> <!-- Специальный дизайн для 404 --> <div v-if="is404" class="not-found"> <h1>404 — Страница не найдена</h1> <p>Возможно, страница была перемещена или удалена.</p> <NuxtLink to="/">Вернуться на главную</NuxtLink> </div>
<!-- Общий дизайн для других ошибок --> <div v-else class="error-generic"> <h1>Ошибка {{ error.statusCode }}</h1> <p>{{ error.message }}</p> <button @click="clearError({ redirect: '/' })">Попробовать снова</button> </div></template>🔒 Обработка ошибок в composables
Заголовок раздела «🔒 Обработка ошибок в composables»export function useApiData<T>(endpoint: string) { const data = ref<T | null>(null) const error = ref<string | null>(null) const loading = ref(false)
const fetch = async () => { loading.value = true error.value = null try { data.value = await $fetch<T>(endpoint) } catch (err: any) { if (err.statusCode === 401) { await navigateTo('/login') } else if (err.statusCode === 404) { error.value = 'Данные не найдены' } else if (err.statusCode >= 500) { // Критическая ошибка — показываем error.vue throw createError({ statusCode: err.statusCode, message: err.message, fatal: true }) } else { error.value = err.message || 'Неизвестная ошибка' } } finally { loading.value = false } }
return { data, error, loading, fetch }}🛡️ NuxtErrorBoundary
Заголовок раздела «🛡️ NuxtErrorBoundary»Компонент для перехвата ошибок в части страницы без показа error.vue:
<template> <NuxtErrorBoundary @error="handleError"> <!-- Основной контент --> <template #default> <ComplexWidget /> </template>
<!-- Fallback при ошибке --> <template #error="{ error, clearError }"> <div class="widget-error"> <p>Виджет недоступен: {{ error.message }}</p> <button @click="clearError()">Повторить</button> </div> </template> </NuxtErrorBoundary></template>
<script setup lang="ts">const handleError = (error: Error) => { console.error('Ошибка виджета:', error) // Не пробрасываем выше — ошибка изолирована}</script>📊 Таблица методов обработки ошибок
Заголовок раздела «📊 Таблица методов обработки ошибок»| Метод | Когда использовать | Поведение |
|---|---|---|
createError() | Создание структурированной ошибки | Бросает или возвращает |
showError() | Показать error.vue программно | Показывает error.vue |
clearError() | Сброс текущей ошибки | Убирает ошибку, опц. редирект |
useError() | Читать текущую ошибку | Реактивное состояние ошибки |
NuxtErrorBoundary | Изолировать ошибки компонента | Локальный fallback |
onErrorCaptured | Vue lifecycle хук | Перехват из дочерних |