iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

Nuxt 3 學習筆記系列 第 27

[Day 27] Nuxt 3 測試 (Testing) 與錯誤處理 (Error handling)

  • 分享至 

  • xImage
  •  

前言

網站開發的過程或部署前若能結合測試將能大大的降低網站出錯的可能性,這篇文章因為官方的測試相關工具尚未完善,故主要針對錯誤處理做講解,處理錯誤固然重要,但能在故障發生之前降低錯誤發生的可能性,測試是必然的,所以還是得稍微簡單提一下。網站上線之後,難免會遇到一些 Bug 導致網站異常,更要命的是一段未被攔截到的錯誤,將導致整個 JavaScript 無法繼續運作,網站也無法繼續互動,使用者全然不知道發生了什麼事情。當然不是所有情況都是自己的鍋,有可能是使用者輸入不合法的參數或瀏覽不存在的頁面,如何的捕獲錯誤並給予適當的提示及引導,也是網站開發相當重要的一環。

Nuxt 3 測試 (Testing)

Nuxt 3 目前的測試工具仍在開發當中,API 和處理行為可能會發生變化,官方也提到,測試工具目前處於預覽階段,尚未準備好可以測試正式環境中的專案,但如果是模組的開發者,可以參考模組作者指南

在 Nuxt 3 中重寫了 @nuxt/test-utils,目前發佈在 @nuxt/test-utils,可以用來支援 VitestJestTest runner

使用 NPM 安裝套件

npm install -D @nuxt/test-utils-edge vitest

在每一個 describe 區塊中,你需要在開始測試之前使用 @nuxt/test-utils-edge 提供的輔助函數,來取得並設置 Test Context,範例程式如下。

import { describe, test } from 'vitest'
import { setup, $fetch } from '@nuxt/test-utils-edge'
describe('My test', async () => {
  await setup({
    // test context options
  })
  test('my test', () => {
    // ...
  })
})

更多的測試 API 、參數選項與使用方式可以參考最新的官方文件

Nuxt 3 錯誤處理 (Error handling)

Vue 渲染生命週期中的錯誤 (SSR + SPA)

當我們啟用 SSR 時,也就是包含了在後端由 Vue 渲染生命週期中,發生的錯誤,可以使用 Vue.js 提供的 onErrorCaptured() 函數來註冊一個 hook,用以捕獲子元件所發生的錯誤。

舉例,新增一個 ButtonOOPS 元件,並添加一個點擊事件程式碼如下:

const onClick = () => {
  throw new Error('由 ButtonOOPS 元件,拋出一個錯誤!')
}

建立一個頁面 ./pages/vue/onErrorCaptured.vue 來使用 ButtonOOPS 按鈕元件,我們使用 onErrorCaptured() 來捕獲程式碼如下:

<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
})
</script>

我們可以在 SFC 中使用 onErrorCaptured() 來捕獲子元件所發生的錯誤,當我們處理完錯誤可以使用 return false 來阻止錯誤冒泡 (Bubbling)。
https://i.imgur.com/4XRKv5L.gif

vueApp.config.errorHandler

我們可以自訂一個插件來接收 Vue 所有的錯誤。

建立 ./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 所捕獲。
https://i.imgur.com/xCBhUbs.gif

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>

https://ithelp.ithome.com.tw/upload/images/20221012/20152617KgY3VvYdh8.png

客戶端

若是在客戶端拋出 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>

https://i.imgur.com/RLcUZot.gif

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" })

小結

測試錯誤處理在網站開發過程與上線後都非常的重要,Nuxt 3 提供了許多方便的組合式函數,可以來處理與拋出錯誤,建議在建立頁面、元件或 Server API 時能盡量攔截捕捉錯誤,並使用 createError 來建立適當的 HTTP Status Code 與錯誤訊息,相信使用者也會降低遇到網站喪失互動性,或莫名需要重新整理才能繼續的使用體驗。


感謝大家的閱讀,這是我第一次參加 iThome 鐵人賽,請鞭小力一些,也歡迎大家給予建議 :)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。

範例程式碼

參考資料


上一篇
[Day 26] Nuxt 3 Public 與 Assets 資源目錄
下一篇
[Day 28] Nuxt 3 渲染模式 (Rendering modes) 與網站使用體驗核心指標 (Core Web Vitals)
系列文
Nuxt 3 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
janshawn
iT邦新手 5 級 ‧ 2022-10-13 17:01:57

希望最後幾篇能教打包上線,感謝大大!

Ryan iT邦新手 2 級 ‧ 2022-10-14 00:10:00 檢舉

最後確實會講到如何建構生產環境的專案,希望能合你胃口 xDDD

0
碼農
iT邦新手 4 級 ‧ 2024-03-06 12:07:34

哈囉Ryan~
請問今年,您有在nuxt3用vitest寫測試嗎?
有沒有打算今年寫測試相關的文章呢~~(敲碗
或加碼一篇關於nuxt官網的testing,寫一篇比較白話的說明文章XD

關於測試時都要將.vue中的相關套件都mock
為了針對.vue中的函式邏輯做測試,
但mock寫法有些微差異,例如有引入i18n或axios的.vue,就要mock
測試的套件也好多,@nuxt/test-utils vitest(or jest) @vue/test-utils happy-dom(or jsdom) playwright-core,都不知道測什麼要用哪個套件的哪個方法
您有收集這類專案或文章可以參考嗎~

我要留言

立即登入留言