🔥【Vue.js → Nuxt 入門推薦!🌟 新書即將上市 🌟】
📘《想要 SSR 嗎?就使用 Nuxt 吧!Nuxt 讓 Vue.js 更好處理 SEO 搜尋引擎最佳化》
👀 Nuxt v4 內容與範例也可以參考並購買本系列文筆著所著書籍
📦 預計於:2025/08/14 出版,目前天瓏書局預購有 7️⃣8️⃣ 折優惠
👉 點此前往購買:https://pse.is/7yulm5
注意:Nuxt 4 已於 2025/07/16 釋出,本文部分內容或範例可能和最新版本有所不同
在 Vue 的專案內我們的頁面通常由 Vue Router 來控制路由及導航,Vue Router 提供了導航守衛 (Navigation Guards) 的 Hook API,讓我們可以在全域、路由甚至是元件中,來控制路由跳轉或取消的方式來守護我們的路由,不讓其隨意導航至特定頁面。Nuxt 3 提供了一個中間件的目錄,讓我們可以製作路由的中間件,來實作出類似導航守衛的效果。
導航守衛就是在訪問頁面之前,會攔截你的路由請求並執行自訂的驗證邏輯,依據驗證的成功與否,准予放行跳轉至路由頁面,抑或是取消訪問該路由,再依照不同處理方式進行中斷或重導向至特定路由頁面。
導航守衛在實務上常見的使用情境,就是拿來做頁面訪問的權限驗證,例如,只有管理員才能訪問 /admin 路由下的頁面,我們可能就會添加攔截的 hook 來驗證使用者是否登入及夾帶的 Token 或 Role 是否有權限可以瀏覽,如果驗證成功就准予瀏覽管理員相關頁面,否則,將路由頁面導回至首頁、登入頁或錯誤頁面,如同一個守衛在路由之間進行把關驗證權限。
我們以 Vue Route v4 來舉例,Vue Route 提供了以下三種情況下可以使用的 hook,分別是在全域、路由或是元件中:
當全域守衛 hook 添加好之後,每次導航至不同路由時,都會攔截以異步的方式執行相對應的處理邏輯。
全域守衛提供了 router.beforeEach() hook 可以在進入任何一個路由前進行攔截處理,當導航觸發時就會依照建立的順序做呼叫,因為是異步函數解析執行,所以在所有的守衛 resolve 之前,會一直處於 pending 的狀態。
而同樣是數全域守衛的 router.beforeResolve() hook 會在所有元件內的導航守衛、路由都被解析及執行完畢後才執行,也就是說這個 router.beforeResolve() 呼叫的時間點晚於 router.beforeEach()。
與 router.beforeEach() hook 相反,全域後置 hooks 提供的 router.afterEach() Hook 會是在路由跳轉結束後才觸發,在這裡路由已經完成跳轉,路由本身也不會再被更動,這個 hook 通常用於分析類或設置頁面相關的資訊等輔助型的功能很有幫助。
與 router.beforeEach() 不同,我們可以為每一個路由添加 beforeEnter() hook,來達到每一個路由頁面有不同的執行方法,同時也只會在不同的路由導航中切換才會觸發。
在元件的內部中,也提供三種 hooks 分別為:
導航守衛 (Navigation Guards) 在導航出發後的 hook 觸發順序如下圖:
Nuxt 3 中提供了一個路由中間件的框架,我們可以在專案下建立名為 middleware 目錄,在這個目錄下我們可以建立中間件,並讓整個 Nuxt 頁面或特定的路由做使用,也可以在頁面中添加,這個中間件可以理解為 Vue Router 中的導航守衛 (Navigation Guards),同樣有 to 與 from 參數用以在導航至特定路由之前驗證權限或執行商業邏輯等。
當我們想要建立路由中間件時,可以在 Nuxt 專案的 middleware 目錄下建立檔案,並預設匯出一個由 defineNuxtRouteMiddleware() 定義的函數,例如:
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation()
  }
  return navigateTo('/')
})
路由中間件能接收目前的路由 to 與下一個路由 from 做為參數,如同 Vue Router 的導航守衛,以此我們就可以來做一些判斷與驗證操作。
Nuxt 提供了兩個全域的 helpers,可以直接從中間件回傳:
在插件或中間件中重新定向到給定的路由。也可以直接呼叫它進行頁面導航。
navigateTo 參數依序為:
可以在中間件中回傳 abortNavigation() 來中止導航,並可以選擇是否傳入錯誤訊息。
abortNavigation 參數為:
與 Vue Router 中的導航守衛稍有不同,在 Nuxt 的中間件中可以使用 navigateTo 與 abortNavigation 來決定導航至新的路由或終止導航,如果中間件沒有回傳任何東西,則表示不阻塞導航,如果有下一個中間件,則而移往下一個功能做執行,或者完成路由導航。
nothing - 不阻塞導航並且會移動到下一個中間件功能,如果有的話,或者完成路由導航
return navigateTo('/')或return navigateTo({ path: '/' })- 重定向到給定路徑,
如果使用 navigateTo() 重定向是發生在伺服器端 ,則將 HTTP Status Code 設置為暫時重定向狀態碼 302 Found。
如果使用 navigateTo() 並夾入 options.redirectCode 屬性,例如 return navigateTo('/', { redirectCode: 301 }),發生的重定向在伺服器端,將 HTTP Status Code 設置為永久重定向狀態碼 301 Moved Permanently。
在 Nuxt 中路由中間件分為以下三種:
不需要建立檔案,通常在路由頁面中使用 definePageMeta() 來定義的中間件,就屬於這種類型。
例如,直接定義一個匿名的中間件在頁面元件中使用:
<script setup>
definePageMeta({
  middleware: defineNuxtRouteMiddleware(() => {
    console.log(`[匿名中間件] 我是直接定義在頁面內的匿名中間件`)
  })
})
</script>
在 middleware 目錄下所建立的中間件,當在頁面使用 definePageMeta() 來指定使用具名的中間件時,將透過異步來自動載入。具名的路由的名稱被規範為是烤肉串式 (Kebab case) 命名。
例如,建立一個 ./middleware/random-redirect.js 中間件檔案:
export default defineNuxtRouteMiddleware(() => {
  if (Math.random() > 0.5) {
    console.log(`[來自 random-redirect 中間件] 重新導向至 /haha`)
    return navigateTo('/haha')
  }
  console.log(`[來自 random-redirect 中間件] 沒發生什麼特別的事情~`)
})
當我們要使用這個中間件時,可以在頁面中使用 definePageMeta() 並傳入 middleware 屬性,來添加路由中間件。
<script setup>
definePageMeta({
  middleware: 'random-redirect'
})
</script>
如果中間件有多個,你也可以使用陣列來傳入多個中間件,並且會依序執行這些路由中間件。
<script setup>
definePageMeta({
  middleware: ['random-redirect', 'other']
})
</script>
當我們在頁面中添加這個中間件後,在切換到這個路由頁面時,約有一半的機會,會被導航至 /haha 頁面。
在具名的中間件的檔名添加後綴 .global,如 auth.global.js,這個路由中間件將會被自動載入,並在每次導航變更時自動執行。
例如,我們建立一個 ./middleware/always-run.global.js 中間件檔案,內容如下:
export default defineNuxtRouteMiddleware((to, from) => {
  console.log(`[全域中間件] to: ${to.path}, from: ${from.path}`)
})
這個全域的路由中間件,將會在每一次導航切換頁面時執行。
你可以使用 addRouteMiddleware() 輔助函數來手動添加全域或命名路由中間件,例如在插件中。
export default defineNuxtPlugin(() => {
  addRouteMiddleware('global-test', () => {
    console.log('這個是由插件添加的全域中間件,並將在每次路由變更時執行')
  }, { global: true })
  addRouteMiddleware('named-test', () => {
    console.log('這個是由插件添加的具名中間件,並將會覆蓋任何現有的同名中間件')
  })
})
在 Nuxt 中我們可以使用所提供的中間件框架,來建立路由頁面中的中間件,而路由中間件會在到特定路由之前執行想要運行的邏輯,對於驗證權限等非常方便,也正是實現導航守衛 (Navigation Guards) 的方式。這篇我們主要講述的路由中間件,也將會與我們之後會提到的伺服器端的中間件有所不同,雖然名稱相似但與 Nitro 啟動時執行的伺服器中間件完全不同。
感謝大家的閱讀,這是我第一次參加 iThome 鐵人賽,請鞭小力一些,也歡迎大家給予建議 :)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。