Vue-Router導航守衛:
有的時候,我們需要通過路由來進行一些操作,比如最常見的登錄權限驗證,當用戶滿足條件時,才讓其進入導航,否則就取消跳轉,並跳到登錄頁面讓其登錄。
爲此我們有很多種方法可以植入路由的導航過程:全局的, 單個路由獨享的, 或者組件級的,推薦優先閱讀路由文檔
全局守衛
vue-router全局有三個守衛:
- router.beforeEach 全局前置守衛 進入路由之前
- router.beforeResolve 全局解析守衛(2.5.0+) 在beforeRouteEnter調用之後調用
- router.afterEach 全局後置鉤子 進入路由之後
使用方法:
// main.js 入口文件
import router from './router'; // 引入路由
router.beforeEach((to, from, next) => {
next();
});
router.beforeResolve((to, from, next) => {
next();
});
router.afterEach((to, from) => {
console.log('afterEach 全局後置鉤子');
});
複製代碼
to,from,next 這三個參數:
to和from是將要進入和將要離開的路由對象,路由對象指的是平時通過this.$route獲取到的路由對象。
next:Function 這個參數是個函數,且必須調用,否則不能進入路由(頁面空白)。
-
next() 進入該路由。
-
next(false): 取消進入路由,url地址重置爲from路由地址(也就是將要離開的路由地址)。
-
next 跳轉新路由,當前的導航被中斷,重新開始一個新的導航。
我們可以這樣跳轉:next('path地址')或者next({path:''})或者next({name:''}) 且允許設置諸如 replace: true、name: 'home' 之類的選項 以及你用在router-link或router.push的對象選項。 複製代碼
路由獨享守衛
如果你不想全局配置守衛的話,你可以爲某些路由單獨配置守衛:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// 參數用法什麼的都一樣,調用順序在全局前置守衛後面,所以不會被全局守衛覆蓋
// ...
}
}
]
})
複製代碼
路由組件內的守衛:
- beforeRouteEnter 進入路由前
- beforeRouteUpdate (2.2) 路由複用同一個組件時
- beforeRouteLeave 離開當前路由時
文檔中的介紹:
beforeRouteEnter (to, from, next) {
// 在路由獨享守衛後調用 不!能!獲取組件實例 `this`,組件實例還沒被創建
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該組件被複用時調用 可以訪問組件實例 `this`
// 舉例來說,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 組件,因此組件實例會被複用。而這個鉤子就會在這個情況下被調用。
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用,可以訪問組件實例 `this`
}
複製代碼
beforeRouteEnter訪問this
因爲鉤子在組件實例還沒被創建的時候調用,所以不能獲取組件實例 this
,可以通過傳一個回調給next
來訪問組件實例 。
但是回調的執行時機在mounted後面,所以在我看來這裏對this的訪問意義不太大,可以放在created
或者mounted
裏面。
beforeRouteEnter (to, from, next) {
console.log('在路由獨享守衛後調用');
next(vm => {
// 通過 `vm` 訪問組件實例`this` 執行回調的時機在mounted後面,
})
}
複製代碼
beforeRouteLeave:
導航離開該組件的對應路由時調用,我們用它來禁止用戶離開,比如還未保存草稿,或者在用戶離開前,將setInterval
銷燬,防止離開之後,定時器還在調用。
beforeRouteLeave (to, from , next) {
if (文章保存) {
next(); // 允許離開或者可以跳到別的路由 上面講過了
} else {
next(false); // 取消離開
}
}
複製代碼
關於鉤子的一些知識:
路由鉤子函數的錯誤捕獲
如果我們在全局守衛/路由獨享守衛/組件路由守衛的鉤子函數中有錯誤,可以這樣捕獲:
router.onError(callback => {
// 2.4.0新增 並不常用,瞭解一下就可以了
console.log(callback, 'callback');
});
複製代碼
在路由文檔中還有更多的實例方法:動態添加路由等,有興趣可以瞭解一下。
跳轉死循環,頁面永遠空白
我瞭解到的,很多人會碰到這個問題,來看一下這段僞代碼:
router.beforeEach((to, from, next) => {
if(登錄){
next()
}else{
next({ name: 'login' });
}
});
複製代碼
看邏輯貌似是對的,但是當我們跳轉到login
之後,因爲此時還是未登錄狀態,所以會一直跳轉到login
然後死循環,頁面一直是空白的,所以:我們需要把判斷條件稍微改一下。
if(登錄 || to.name === 'login'){ next() } // 登錄,或者將要前往login頁面的時候,就允許進入路由
複製代碼
全局後置鉤子的跳轉:
文檔中提到因爲router.afterEach不接受next
函數所以也不會改變導航本身,意思就是隻能當成一個鉤子來使用,但是我自己在試的時候發現,我們可以通過這種形式來實現跳轉:
// main.js 入口文件
import router from './router'; // 引入路由
router.afterEach((to, from) => {
if (未登錄 && to.name !== 'login') {
router.push({ name: 'login' }); // 跳轉login
}
});
複製代碼
額,通過router.beforeEach 也完全可以實現且更好,我就騷一下。
完整的路由導航解析流程(不包括其他生命週期):
- 觸發進入其他路由。
- 調用要離開路由的組件守衛
beforeRouteLeave
- 調用局前置守衛:
beforeEach
- 在重用的組件裏調用
beforeRouteUpdate
- 調用路由獨享守衛
beforeEnter
。 - 解析異步路由組件。
- 在將要進入的路由組件中調用
beforeRouteEnter
- 調用全局解析守衛
beforeResolve
- 導航被確認。
- 調用全局後置鉤子的
afterEach
鉤子。 - 觸發DOM更新(
mounted
)。 - 執行
beforeRouteEnter
守衛中傳給 next 的回調函數
鏈接:https://juejin.im/post/5b41bdef6fb9a04fe63765f1