導航守衛(Navigation Guard)可以在 3 個地方使用,包括全域、元件和路由。所謂導航守衛就是在訪問頁面之前,會像一個守衛攔截並執行你所設定的任務,執行後會跳轉或取消訪問該路由。常用情況例如是登入驗證,意思是在用戶載入頁面前,驗證此用戶的 token 是否有效,有就放行,沒有就導回登入頁。另外,導航守衛有不同的 Hook 函式可使用,就如 Vue 也有它的生命週期(created
、 mounted
等等)。但注意,不同的守衛各自都有自己一套 Hook 方法。
注意:Vue 3 是使用 Vue Router 4.x。Vue 2 是使用 Vue Router 3.x。 兩者某些語法會有不同。因此在查看文件時務必留意!
以下主要講解 Vue 3 (Vue Router 4.x)的做法。今天主題比較直白,的確沒有什麼概念要理解,但也會舉出自己曾經使用過的例子作解說。
每次進入任何一個路由前都會作攔截,並執行你在導航守衛裏寫下的任務。這些函式同樣會以非同步方式執行。
執行次序:
使用情景:身分驗證。
beforeEach()
一樣,在進入路由前呼叫。但它會在 beforeEach()
後,並且在所有路由、元件的導航守衛都執行完,最後才會呼叫並執行。使用情景:呼叫 API,取得遠端資料。
使用情景:像 GA 追蹤工具一樣記錄使用者瀏覽紀錄。
結果跳轉路由後才呼叫。
Hook 函式如下:
const router = createRouter({ ... })
router.beforeEach(async (to, from, next) => {
// to:使用者要跳轉的路由
// from:使用者前一個訪問的路由
// 回傳 false 取消跳轉,true / undefined(預設)容許跳轉
return false
// next 參數在 Vue Router 4 並非必須
})
router.beforeResolve(async (to, from, next) => {
// do something...
})
router.afterEach((to, from, failure) => {
// 因為 afterEach 是在路由跳轉結束後才觸發,因此沒有 next
// failure 參數表示路由跳轉失敗
})
注意:
next
參數,在 Vue Router 3 (即是 Vue 2 )是必須,否則 beforeEach
這個 hook 函式就不會被 resolve 並且報錯。官方例子如下:router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
關於在 Vue Router 3 的 next 參數其他用法,詳情參考官方文件。
在 4.x 版本的 Vue Router 文件裏,官方表示有機會移除 next 參數。因此不建議使用。
這裏簡單示範一下自己使用導航守衛來驗證身份的做法。我當時是透過在 cookies 存放使用者登入時所取得的 token,來證明此使用者已登入。換言之,如果在 cookies 裏沒有 token,或者 token 失效,代表此使用者目前沒登入,並需要跳轉回登入頁。
以下例子,我使用了 js-cookie 插件來操作 cookie。
router / index.js
import Cookies from 'js-cookie';
consr routes = [
{
path: '/',
component: Login // 首頁是登入頁
},
...
]
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 使用全域導航守衛
// 每次跳轉頁面都檢查是否有 cookies
router.beforeEach(to => {
const token = Cookies.get('userToken');
// 登入頁(首頁)不用驗證
if (to.fullPath === '/') return;
if (!token || !validateToken(token)) {
// 可能是因為 token 無效,所以需要移除此使用者的 token
Cookies.remove('userToken');
return '/';
}
}
// 驗證成功,可以放行
return true;
});
如果沒有加 if (to.fullPath === '/') return;
,就會出現死循環問題。因為跳轉到首頁時,守衛發現首頁也沒有驗證成功,於是再次跳轉到頁面,造成無限循環:
[Vue Router warn]: Detected an infinite redirection in a navigation guard when going from "/" to "/". Aborting to avoid a Stack Overflow. This will break in production if not fixed.
[Vue Router warn]: Unexpected error when starting the router: Error: Infinite redirect in navigation guard at eval
只有在進入某一路由時,才會觸發此導航守衛。 重點如下:
router.beforeEnter()
這 Hook 函式。官方例子:
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
注意:如果該路由的 params、query、hash 有變化,也不會觸發守衛。例如 /user/1
到 /users/2
,或者 /user/1/#info
到 /user/2/#detail
。
遇到要重用守衛的情況,可使用陣列。
官方例子:
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
在某元件設定守衛。元件守衛可使用的 Hook 函式:
this
。
this
。寫法與在元件裏使用生命週期一樣:
data() {
return { ... }
},
beforeRouteEnter(to, from, next) {
// 無法使用 this
// 但可以透過 next 使用 this
next( vm => { ... })
},
beforeRouteUpdate(to, from) {
// do something...
},
beforeRouteLeave(to, from) {
// do something...
},
Vue 官網有提到所有 Hook 的執行次序,在此就不重複了:
截圖至官方文件
重新認識 Vue.js - 4-4 路由守衛(Navigation Guards)
Vue Router - 导航守卫