mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
feat(nuxt3): add <NuxtErrorBoundary>
component for fine-grained error handling (#3671)
* feat(nuxt3): add `<NuxtErrorBoundary>` component for fine-grained error handling * feat: add `@error` event handling * fix: don't clear error on nav * fix: remove `clearError` wrapper * fix: remove outdated implementation * update clear error * upddate example with FaultyComponent Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
parent
6d2625925f
commit
12304909bc
@ -88,3 +88,32 @@ You can call this function at any point on client-side, or (on server side) dire
|
||||
* `function clearError (redirect?: string): Promise<void>`
|
||||
|
||||
This function will clear the currently handled Nuxt error. It also takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page).
|
||||
|
||||
## Rendering errors within your app
|
||||
|
||||
Nuxt also provides a `<NuxtErrorBoundary>` component that allows you to handle client-side errors within your app, without replacing your entire site with an error page.
|
||||
|
||||
This component is responsible for handling errors that occur within its default slot. On client-side, it will prevent the error from bubbling up to the top level, and will render the `#error` slot instead.
|
||||
|
||||
The `#error` slot will receive `error` as a prop. (If you set `error = null` it will trigger re-rendering the default slot; you'll need to ensure that the error is fully resolved first or the error slot will just be rendered a second time.)
|
||||
|
||||
::alert{type="info"}
|
||||
If you navigate to another route, the error will be cleared automatically.
|
||||
::
|
||||
|
||||
### Example
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<template>
|
||||
<!-- some content -->
|
||||
<NuxtErrorBoundary @error="someErrorLogger">
|
||||
<!-- You use the default slot to render your content -->
|
||||
<template #error="{ error }">
|
||||
You can display the error locally here.
|
||||
<button @click="error = null">
|
||||
This will clear the error.
|
||||
</button>
|
||||
</template>
|
||||
</NuxtErrorBoundary>
|
||||
</template>
|
||||
```
|
||||
|
@ -21,6 +21,9 @@ function triggerError () {
|
||||
<NuxtLink to="/" class="n-link-base">
|
||||
Home
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/other" class="n-link-base">
|
||||
Other
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/404" class="n-link-base">
|
||||
404
|
||||
</NuxtLink>
|
||||
@ -36,6 +39,8 @@ function triggerError () {
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<FaultyComponent />
|
||||
|
||||
<template #footer>
|
||||
<div class="text-center p-4 op-50">
|
||||
Current route: <code>{{ route.path }}</code>
|
||||
|
25
examples/with-errors/components/FaultyComponent.vue
Normal file
25
examples/with-errors/components/FaultyComponent.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
const hasIssue = ref(true)
|
||||
|
||||
const fixIssue = (error) => {
|
||||
hasIssue.value = false
|
||||
error.value = null
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtErrorBoundary>
|
||||
<throw-error v-if="hasIssue" />
|
||||
<div v-else>
|
||||
Component is working ^_^
|
||||
</div>
|
||||
|
||||
<template #error="{ error }">
|
||||
Component failed to Render -_-
|
||||
<button @click="fixIssue(error)">
|
||||
(fix the issue)
|
||||
</button>
|
||||
</template>
|
||||
</NuxtErrorBoundary>
|
||||
</template>
|
7
examples/with-errors/components/ThrowError.vue
Normal file
7
examples/with-errors/components/ThrowError.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<script setup>
|
||||
throw new Error('Deliberate error by <ThrowError>')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Should never see this</div>
|
||||
</template>
|
@ -4,12 +4,15 @@
|
||||
<h1>{{ error.message }}</h1>
|
||||
There was an error 😱
|
||||
|
||||
<br>
|
||||
<button @click="handleError">
|
||||
Clear error
|
||||
</button>
|
||||
<br>
|
||||
<NuxtLink to="/404">
|
||||
Trigger another error
|
||||
</NuxtLink>
|
||||
<br>
|
||||
<NuxtLink to="/">
|
||||
Navigate home
|
||||
</NuxtLink>
|
||||
|
0
examples/with-errors/pages/other.vue
Normal file
0
examples/with-errors/pages/other.vue
Normal file
19
packages/nuxt3/src/app/components/nuxt-error-boundary.ts
Normal file
19
packages/nuxt3/src/app/components/nuxt-error-boundary.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { defineComponent, ref, onErrorCaptured } from 'vue'
|
||||
import { useNuxtApp } from '#app'
|
||||
|
||||
export default defineComponent({
|
||||
setup (_props, { slots, emit }) {
|
||||
const error = ref(null)
|
||||
const nuxtApp = useNuxtApp()
|
||||
|
||||
onErrorCaptured((err) => {
|
||||
if (process.client && !nuxtApp.isHydrating) {
|
||||
emit('error', err)
|
||||
error.value = err
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
return () => error.value ? slots.error?.({ error }) : slots.default?.()
|
||||
}
|
||||
})
|
@ -84,6 +84,12 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
filePath: resolve(nuxt.options.appDir, 'components/layout')
|
||||
})
|
||||
|
||||
// Add <NuxtErrorBoundary>
|
||||
addComponent({
|
||||
name: 'NuxtErrorBoundary',
|
||||
filePath: resolve(nuxt.options.appDir, 'components/nuxt-error-boundary')
|
||||
})
|
||||
|
||||
// Add <ClientOnly>
|
||||
addComponent({
|
||||
name: 'ClientOnly',
|
||||
|
Loading…
Reference in New Issue
Block a user