error-handling
Nuxt 3 錯誤處理 (Error handling)
Nuxt 3 是一個全端的框架 (Full-stack Framework),除了在客戶端 Vue 渲染生命週期中的錯誤外,也包含了伺服器端的 SSR 過程、Server API 或是 Nitro Engine 運作過程中的錯誤,大致可以區分為下列三種:
- Vue 渲染生命週期中的錯誤 (SSR + SPA)
- 伺服器端 (Server) 和客戶端 (Client) 啟動時發生的錯誤 (SSR + SPA)
- API 或 Nitro Engine 伺服器端生命週期中的錯誤
Vue 渲染生命週期中的錯誤 (SSR + SPA)
onErrorCaptured
當我們啟用 SSR 時,也就是包含了在後端由 Vue 渲染生命週期中,發生的錯誤,可以使用 Vue.js 提供的 onErrorCaptured() 函數來註冊一個 hook,用以捕獲子元件所發生的錯誤。
舉例,新增一個 ButtonOOPS 元件,並添加一個點擊事件程式碼如下:
const onClick = () => {
throw new Error("由 ButtonOOPS 元件,拋出一個錯誤!");
};
使用 ButtonOOPS 按鈕元件,使用 onErrorCaptured() 來捕獲程式 碼如下:
<!-- ./pages/vue/onErrorCaptured.vue -->
<template>
<div class="flex flex-col items-center bg-white py-24">
<ButtonOOPS />
<div class="mt-4 text-red-500">{{ errorMessage }}</div>
</div>
</template>
<script setup>
import { onErrorCaptured } from "vue";
const errorMessage = ref();
onErrorCaptured((err) => {
console.error("[捕獲錯誤]", err.message);
errorMessage.value = err.message;
return false; // 為了不要把 error 冒泡到上層
});
</script>
我們可以在 SFC 中使用 onErrorCaptured() 來捕獲子元件所發生的錯誤,當我們處理完錯誤可以使用 return false 來阻止錯誤冒泡 (Bubbling)。

vueApp.config.errorHandler
Nuxt 提供了一個 vue:error 的 hook,如果 Vue 中發生的錯誤冒泡傳播到頂層,就會呼叫這個 hook,若有使用錯誤處理或回報的框架,我們可以自訂一個插件來接收 Vue 所有的錯誤。這個算是個 global 的 error catch
// ./plugins/vueErrorHandle.js
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.config.errorHandler = (error) => {
console.error("[由 vueErrorHandle 插件捕獲的錯誤]", error);
};
});
建立一個頁面 ./pages/vue/thowError.vue 來使用 ButtonOOPS 按鈕元件程式碼如下,但不捕獲錯誤,將錯誤繼續冒泡:
<template>
<div class="flex flex-col items-center bg-white py-24">
<ButtonOOPS />
</div>
</template>
元件中所發生的錯誤,不論是否有被處理,只要錯誤冒泡至頂層,就會被 vueErrorHandle 插件所定義的 vue:error 的 hook 所捕獲。

伺服器端 (Server) 和客戶端 (Client) 啟動時發生的錯誤 (SSR + SPA)
如果 Nuxt 在啟動時發生錯誤時,將會呼叫 Nuxt 實例的 app:error hook,你可以用來處理下列幾種錯誤情況:
- 執行 Nuxt 插件時
- 在
app:created、app:beforeMount或app:mountedhooks 處理時發生的錯誤。 - 在客戶端 Mounting Vue App。但建議使用 Vue.js 的
onErrorCaptured或 Nuxt 的vue:errorhook 進行處理。
更多的 Lifecycle Hooks 可以參考官方文件。
API 或 Nitro Engine 伺服器端生命週期中的錯誤
目前無法在伺服器端定義 Server API 或 Nitro Engine 的生命週期中發生錯誤時的錯誤處理,但可以呈現錯誤的頁面。
Nuxt 3 呈現錯誤頁面
當 Nuxt 遇到致命的錯誤時,不論是在伺服器生命週期期間,抑或在前端或後端渲染 Vue App 時,它都會渲染一個 HTML 錯誤頁面。
例如,我們前往一個不存在的頁面 http://localhost:3000/omg,瀏覽器將會渲染出一個 Nuxt 預設的錯誤頁面。

而當 HTTP 請求帶有 Accept: application/json 標頭,則回傳一個 JSON 的物件來描述錯誤訊息。

當然,如果你還記得頁面 (Pages) 與路由 (Routing)的設定,我們可以自己建立一個 ./pages/404.vue 來處理找不到路由的錯誤頁面。不過呢,在 Nuxt 與 Vue App 中可能發生的錯誤會更多,所以會由其他頁面進行錯誤頁面的渲染。
Nuxt 3 自訂錯誤頁面
Nuxt 預設的錯誤頁面,我們可以在專案目錄下建立 error.vue,注意這個頁面檔案與 app.vue 同層級,專案目錄下其他檔案就先不列,同層級意思就是結構大概長的像下面這樣。
nuxt-app/ ├── pages.vue └── error.vue
error.vue 同時也具有一個 error 的屬性 (Props),提供我們進行處理。
<script setup>
const props = defineProps({
error: Object,
});
</script>
當定義好自訂的錯誤頁面,我們可以呼叫 clearError() 函數來清除錯誤並重導向至首頁 / 或其他安全的頁面。
<template>
<div class="flex flex-col items-center bg-white py-24">
<h1 class="my-12 text-5xl font-semibold text-red-500">發生了錯誤!</h1>
<div class="flex flex-col items-center justify-center">
<p class="text-xl text-gray-500">{{ error.message }}</p>
<button
type="button"
class="mt-8 block rounded-md border border-transparent bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
@click="handleError"
>
清除錯誤後回到首頁
</button>
</div>
</div>
</template>
<script setup>
defineProps({
error: Object,
});
const handleError = () => clearError({ redirect: "/" });
</script>
這邊也要特別注意使用 clearError() 函數來清除錯誤,在使用 Nuxt 任何插件,例如 Vue Router,我們的頁面發生了錯誤後一定要檢查並處理,如果沒有清除錯誤,那麼 Vue App 將不會重新正常的執行。
搭配布局自訂錯誤頁面
建立一個 ./layouts/error.vue 布局,程式碼如下:
<template>
<div class="flex flex-col items-center bg-white py-24">
<h1 class="my-6 text-5xl font-semibold text-red-500">發生了錯誤!</h1>
<slot />
</div>
</template>
調整 ./error.vue 內容如下,注意這邊需要使用 definePageMeta({ layout: false }) 取得布局的完整控制權後,搭配 <NuxtLayout name="error">:
<template>
<NuxtLayout name="error">
<div class="flex flex-col items-center justify-center">
<p class="text-xl text-gray-500">{{ error.message }}</p>
<button
type="button"
class="mt-8 block rounded-md border border-transparent bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
@click="handleError"
>
清除錯誤後回到首頁
</button>
</div>
</NuxtLayout>
</template>
<script setup>
defineProps({
error: Object,
});
const handleError = () => clearError({ redirect: "/" });
definePageMeta({
layout: false,
});
</script>
如此一來我們也能在 error.vue 頁面中使用自訂義的 error 布局。
因為專案有搭配 Tailwind CSS,記得也要把
'./error.{js,ts,vue}'添加至tailwind.config.js設定檔的content屬性中,才能正確的套用樣式類型。
Nuxt 3 錯誤處理的輔助函數
useError
參數類型:
function useError(): Ref<Error | { url; statusCode; statusMessage; message; description; data }>;
此方法將回傳 Nuxt 正在處理的全域錯誤。
使用範例:
const error = useError();
clearError
參數類型:
function clearError(options?: { redirect?: string }): Promise<void>;
此函數將清除目前 Nuxt 處理中的錯誤,函數可以傳入一個路徑來進行重導向,例如清除錯誤後,可以導向至首頁或其他安全的頁面。
使用範例:
clearError();
// or
clearError({ redirect: "/safe" });
createError
參數類型:
function createError(err: { cause; data; message; name; stack; statusCode; statusMessage; fatal }): Error;
此函數可以建立一個帶有附加錯誤資訊的物件,它可以在 Vue 或 Nitro 中使用,並且可以使用 throw 拋出這個錯誤物件。
如果我們使用 createError 建立一個錯誤物件,在伺服器端或客戶端會有不同的效果。
伺服器端
在伺服器端拋出 createError() 建立的錯誤物件時,它將觸發一個全螢幕的錯誤,你可以使用 clearError() 來清除這個錯誤。
使用範例:
<script setup>
throw createError({
statusCode: 500,
statusMessage: "Internal Server Error",
});
</script>

客戶端
若是在客戶端拋出 createError() 建立的錯誤物件,它將會拋出一個非致命的錯誤提供處理,如果想觸發全螢幕的錯誤頁面,可以設定 fatal 屬性為 true。
使用範例:
<script setup>
import { onErrorCaptured } from "vue";
onErrorCaptured((err) => {
console.error("[捕獲錯誤]", err.message);
throw createError({
statusCode: 400,
statusMessage: "Bad Request",
fatal: true,
});
});
</script>

showError
參數類型:
function showError(err: string | Error | { statusCode; statusMessage }): Error;
你可以在客戶端的任何地方呼叫此函數,伺服器端則可以在中間件、插件或 setup() 函數中呼叫。它將觸發一個全螢幕的錯誤頁面,你可以使用 clearError() 來清除錯誤。
因為錯誤頁面與 createError() 觸發的頁面相同,官方也建議改為使用 throw createError() 來建立與拋出錯誤。
使用範例:
showError("? Oh no, an error has been thrown.");
showError({ statusCode: 404, statusMessage: "Page Not Found" });