Skip to content

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.

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 появляется снова, пока она выполняется), а не просто повторно показывает устаревшее состояние.