Справочник API
<ErrorBoundary>
| Проп | Тип | По умолчанию | Описание |
|---|---|---|---|
resetKeys | unknown[] | — | Когда любое значение меняется (сравнение через Object.is), граница автоматически сбрасывается — та же идея, что и resetKeys у react-error-boundary |
resetOnPropsChange | boolean | false | Сброс при изменении ссылки на любой проп, а не только resetKeys |
beforeReset | () => void | — | Вызывается непосредственно перед сбросом (автоматическим или ручным). Не называется onReset — см. примечание ниже |
isolate | boolean | true | false позволяет ошибке дополнительно распространиться к ближайшему предку <ErrorBoundary> |
maxRetries | number | без ограничений | По достижении лимита canRetry в слоте fallback становится false |
reporter | ErrorReporter | ErrorReporter[] | — | Репортер(ы), вызываемые один раз на каждую перехваченную ошибку — см. Адаптеры отчётности |
shouldCatch | (error: CapturedError) => boolean | — | Верните false, чтобы ошибка прошла через эту границу нетронутой — без изменения состояния, без отчёта, без fallback — как будто границы здесь вообще нет. См. Игнорирование конкретных ошибок |
internalErrorPrefix | string | '[vue-error-boundary-kit]' | Префикс для страховочного лога, выводимого, когда ваш собственный beforeReset/репортер сам выбрасывает ошибку. Передайте '', чтобы убрать его |
Почему
beforeReset, а неonReset? Этот компонент также генерирует событиеreset, а Vue выводитonResetкак проп-слушатель этого события. Объявленный проп с тем же именем конфликтовал бы с ним:emit()во Vue обращается кprops.onResetнезависимо от того, объявлен ли он «по-настоящему» как проп, — поэтому он срабатывал бы дважды за сброс, причём второй, вызванный через emit, происходит вне собственного try/catch пакета, полностью обходя защиту от рекурсии, если он выбросит ошибку.beforeResetструктурно избегает этого конфликта.
События:
error(error: CapturedError)— генерируется при каждой перехваченной ошибке, включая всплывшие от дочерней границы сisolate: false.reset()— генерируется при каждом ручном или автоматическом сбросе.
Слоты:
default— обычный контент.fallback— скоуп-слот:{ error, reset, retry, retryCount, canRetry }.reset()также сбрасывает счётчик попыток;retry()увеличиваетretryCountи заново пытается отрисоватьdefault, не сбрасывая счётчик.
Доступно через template-ref: error, hasError, retryCount, canRetry, reset(), retry() — то же состояние и методы, что получает слот fallback, но доступные извне (например, элемент управления повтором, расположенный в другом месте интерфейса, или предок, восстанавливающий границу, для которой он не рендерит fallback). Вызов reset()/retry() на границе, не находящейся в состоянии ошибки, — безвредный no-op.
<script setup>
const boundary = ref()
</script>
<template>
<ErrorBoundary ref="boundary">…</ErrorBoundary>
<button @click="boundary?.reset()">Сброс из другого места</button>
</template>Повтор с задержкой (backoff)
retry() срабатывает мгновенно и безусловно — в большинстве случаев этого достаточно, но для временной/сетевой ошибки повтор в момент клика по кнопке обычно снова заканчивается неудачей тем же образом. vue-error-boundary-kit/retry-backoff оборачивает retry() из template-ref в возрастающую задержку:
import { createBackoffRetry } from 'vue-error-boundary-kit/retry-backoff'
const boundary = useTemplateRef('boundary')
const backoff = createBackoffRetry(boundary, { baseDelayMs: 1000, factor: 2, maxDelayMs: 30_000 })<ErrorBoundary ref="boundary">
<template #fallback="{ error }">
<button :disabled="backoff.isPending.value" @click="backoff.retry()">
{{ backoff.isPending.value ? 'Повтор…' : 'Повторить' }}
</button>
</template>
</ErrorBoundary>Задержка вычисляется на основе собственного retryCount границы (baseDelayMs * factor ** retryCount, с ограничением сверху в maxDelayMs), так что каждая следующая попытка ждёт дольше. cancel() отменяет ожидающий повтор. Работает и с ref от <AsyncBoundary> — оба предоставляют одну и ту же форму { retry, retryCount }.
<AsyncBoundary>
Отдельная точка входа (vue-error-boundary-kit/async-boundary) — не входит в основной бандл, поэтому ничего не стоит, если вы её не импортируете. Объединяет <Suspense> и <ErrorBoundary>, которые сегодня иначе пришлось бы вкладывать друг в друга вручную:
import { AsyncBoundary } from 'vue-error-boundary-kit/async-boundary'<AsyncBoundary :reset-keys="[userId]">
<template #default>
<UserProfile :id="userId" />
<!-- допустим async setup() / асинхронные компоненты -->
</template>
<template #loading>
<Spinner />
</template>
<template #fallback="{ error, retry }">
<ErrorState :message="error.message" @retry="retry" />
</template>
</AsyncBoundary>Это композиция, а не переизобретение: внутри это <ErrorBoundary>, оборачивающий <Suspense>, поэтому он принимает все пропы <ErrorBoundary> (resetKeys, maxRetries, reporter, shouldCatch, …), генерирует те же события error/reset и предоставляет через template-ref те же error/hasError/retryCount/canRetry/reset()/retry() — всё это обрабатывается той же единственной реализацией onErrorCaptured, что уже есть у <ErrorBoundary>. Единственное, что добавляется, — слот loading, отображаемый, пока асинхронные зависимости слота default ещё не разрешились. retry()/reset() перемонтируют слот default, поэтому повторённая асинхронная операция действительно выполняется заново (слот loading появляется снова, пока она выполняется), а не просто повторно показывает устаревшее состояние.
useErrorBoundary()
Для программного использования вне шаблонного <ErrorBoundary> — например, для собственного состояния ошибки на уровне layout в Nuxt или для регистрации ошибок из кода, который errorCaptured никогда не увидит:
const { error, hasError, reset, captureError } = useErrorBoundary({
onError: (e) => report(e),
reporter: myReporter,
})
try {
JSON.parse(untrustedInput)
} catch (err) {
captureError(err, { source: 'manual', componentName: 'ImportPanel' })
}captureError(err, info?)— регистрирует ошибку вручную; возвращает получившийсяCapturedError.reset()— сбрасываетerrorобратно вnull.error: ShallowRef<CapturedError | null>,hasError: ComputedRef<boolean>.
Опции: onError, beforeReset (вызывается непосредственно перед тем, как reset() очистит состояние), reporter, reportContext, internalErrorPrefix (см. примечание к <ErrorBoundary> выше — то же значение по умолчанию, та же логика, хотя здесь нет риска конфликта имён, поскольку это обычный composable, а не компонент с собственным emit reset).
useGlobalErrorCapture()
Отдельная точка входа (vue-error-boundary-kit/global-capture) — не подключается по умолчанию, поэтому ничего не стоит в бандлах, которые её не импортируют (включая SSR-бандлы, где это no-op при отсутствии window). Подключает window.addEventListener('error', …) и unhandledrejection, пропуская их через тот же механизм репортер/onError:
import { useGlobalErrorCapture } from 'vue-error-boundary-kit/global-capture'
useGlobalErrorCapture({
reporter: myReporter,
onError: (e) => console.warn('uncaught:', e),
})Опции: onError, reporter, reportContext, shouldCatch, internalErrorPrefix, captureErrors (по умолчанию true), captureRejections (по умолчанию true). Возвращает { stop }; очистка также запускается автоматически, если вызвана внутри активного effect scope (например, в setup() компонента).
Types
interface CapturedError {
error: unknown
message: string
stack?: string
componentName?: string
lifecycleHook?: string
source: 'render' | 'async' | 'event' | 'unhandledrejection' | 'manual'
timestamp: number
}
interface ErrorReporter {
report(error: CapturedError, context?: Record<string, unknown>): void | Promise<void>
}Заметки по source:
'render'— синхронный сбой во время рендера компонента или (синхронного)setup().'async'—async setup(), отклонившийся послеawait. Vue сообщает об обоих случаях идентичной строкой"setup function", поэтому пакет дополнительно проверяет, является ли собственныйsetupкомпонентаAsyncFunction, чтобы различить их.'event'— скомпилированный Vue обработчикv-on(нативное DOM-событие илиemitкомпонента), который выбрасывает ошибку. Обратите внимание: Vue действительно пропускает их черезonErrorCaptured— то, что он по-настоящему не видит, описано ниже.'unhandledrejection'/ и не-Vue'event'— производятся толькоuseGlobalErrorCapture().'manual'— значение по умолчанию дляcaptureError(), еслиsourceне задан.