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>`
|
* `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).
|
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">
|
<NuxtLink to="/" class="n-link-base">
|
||||||
Home
|
Home
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
<NuxtLink to="/other" class="n-link-base">
|
||||||
|
Other
|
||||||
|
</NuxtLink>
|
||||||
<NuxtLink to="/404" class="n-link-base">
|
<NuxtLink to="/404" class="n-link-base">
|
||||||
404
|
404
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
@ -36,6 +39,8 @@ function triggerError () {
|
|||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<FaultyComponent />
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="text-center p-4 op-50">
|
<div class="text-center p-4 op-50">
|
||||||
Current route: <code>{{ route.path }}</code>
|
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>
|
<h1>{{ error.message }}</h1>
|
||||||
There was an error 😱
|
There was an error 😱
|
||||||
|
|
||||||
|
<br>
|
||||||
<button @click="handleError">
|
<button @click="handleError">
|
||||||
Clear error
|
Clear error
|
||||||
</button>
|
</button>
|
||||||
|
<br>
|
||||||
<NuxtLink to="/404">
|
<NuxtLink to="/404">
|
||||||
Trigger another error
|
Trigger another error
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
<br>
|
||||||
<NuxtLink to="/">
|
<NuxtLink to="/">
|
||||||
Navigate home
|
Navigate home
|
||||||
</NuxtLink>
|
</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')
|
filePath: resolve(nuxt.options.appDir, 'components/layout')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add <NuxtErrorBoundary>
|
||||||
|
addComponent({
|
||||||
|
name: 'NuxtErrorBoundary',
|
||||||
|
filePath: resolve(nuxt.options.appDir, 'components/nuxt-error-boundary')
|
||||||
|
})
|
||||||
|
|
||||||
// Add <ClientOnly>
|
// Add <ClientOnly>
|
||||||
addComponent({
|
addComponent({
|
||||||
name: 'ClientOnly',
|
name: 'ClientOnly',
|
||||||
|
Loading…
Reference in New Issue
Block a user