Skip to content

Справочник API

<ErrorBoundary>

ПропТипПо умолчаниюОписание
resetKeysunknown[]Когда любое значение меняется (сравнение через Object.is), граница автоматически сбрасывается — та же идея, что и resetKeys у react-error-boundary
resetOnPropsChangebooleanfalseСброс при изменении ссылки на любой проп, а не только resetKeys
beforeReset() => voidВызывается непосредственно перед сбросом (автоматическим или ручным). Не называется onReset — см. примечание ниже
isolatebooleantruefalse позволяет ошибке дополнительно распространиться к ближайшему предку <ErrorBoundary>
maxRetriesnumberбез ограниченийПо достижении лимита canRetry в слоте fallback становится false
reporterErrorReporter | ErrorReporter[]Репортер(ы), вызываемые один раз на каждую перехваченную ошибку — см. Адаптеры отчётности
shouldCatch(error: CapturedError) => booleanВерните false, чтобы ошибка прошла через эту границу нетронутой — без изменения состояния, без отчёта, без fallback — как будто границы здесь вообще нет. См. Игнорирование конкретных ошибок
internalErrorPrefixstring'[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.

vue
<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 в возрастающую задержку:

ts
import { createBackoffRetry } from 'vue-error-boundary-kit/retry-backoff'

const boundary = useTemplateRef('boundary')
const backoff = createBackoffRetry(boundary, { baseDelayMs: 1000, factor: 2, maxDelayMs: 30_000 })
vue
<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>, которые сегодня иначе пришлось бы вкладывать друг в друга вручную:

ts
import { AsyncBoundary } from 'vue-error-boundary-kit/async-boundary'
vue
<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 никогда не увидит:

ts
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:

ts
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

ts
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 не задан.