一、什么是導航守衛
1、官方的定義
提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中:全局的, 單個路由獨享的, 或者組件級的。
2、自己的理解
導航守衛就是路由跳轉過程中的一些鈎子函數,再直白點路由跳轉是一個大的過程,這個大的過程分為跳轉前中后等等細小的過程,在每一個過程中都有一函數,這個函數能讓你操作一些其他的事兒的時機,比如跳轉前是否驗證登錄等,這就是導航守衛。
二、使用導航守衛
1、安裝包
npm導入
npm install vue-router --save
cnpm淘寶鏡像導入
cnpm install vue-router --save
如果沒有安裝淘寶鏡像需要先安裝
npm install -g cnpm --registry=https://registry.npm.taobao.org
2、全局守衛
是指路由實例上直接操作的鈎子函數,他的特點是所有路由配置的組件都會觸發,直白點就是觸發路由就會觸發這些鈎子函數。鈎子函數按執行順序包括beforeEach、beforeResolve(2.5+)、afterEach三個
beforeEach:
在路由跳轉前觸發,參數包括to,from,next(參數會單獨介紹)三個,這個鈎子作用主要是用於登錄驗證,也就是路由還沒跳轉提前告知,以免跳轉了再通知就為時已晚。
beforeResolve(2.5+)
這個鈎子和beforeEach類似,也是路由跳轉前觸發,參數也是to,from,next三個,和beforeEach區別官方解釋為:
區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
即在 beforeEach 和 組件內beforeRouteEnter 之后,afterEach之前調用。
afterEach:
和beforeEach相反,他是在路由跳轉完成后觸發,參數包括to,from沒有了next(參數會單獨介紹),他發生在beforeEach和beforeResolve之后,beforeRouteEnter(組件內守衛,后講)之前。
他們的使用如下:
router.beforeEach((to, from, next) => {
//to 將要訪問的路徑
//from 代表從哪個路徑跳轉而來
//next 是一個函數,表示放行
// next() 放行 next('/login') 強制跳轉
})
小案例:
比如我們的后台管理系統,基本上都是需要登錄后才能操作的,所以路由跳轉前先判斷是否登錄,如果未登錄狀態就強制跳轉到登錄頁,代碼如下
router 模塊
import Vue from "vue";
import VueRouter from "vue-router";
import Login from "../components/Login";
import Home from "../components/Home";
import Welcome from "../components/Welcome";
import User from "../components/User/User";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", redirect: "/login" },
{ path: "/login", component: Login },
{
path: "/home", component: Home,
redirect: 'welcome',
children: [
{ path: '/welcome', component: Welcome },
{ path: '/users', component: User },
]
}
]
});
//掛載路由導航守衛,控制頁面訪問權限
router.beforeEach((to, from, next) => {
if (to.path === '/login') return next();
//獲取token
const tokenStr = window.sessionStorage.getItem('token')
if (!tokenStr) return next('/login')
next()
})
export default router;
3、單個路由獨享
是指在單個路由配置的時候也可以設置的鈎子函數,其位置就是下面示例中的位置,也就是像Foo這樣的組件都存在這樣的鈎子函數。目前他只有一個鈎子函數beforeEnter。
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
beforeEnter
和 beforeEach
完全相同,如果都設置則在beforeEach
之后緊隨執行,參數to、from、next
小案例
當只有進入某個路由時,才需要驗證,使用路由元信息
在路由配置中加入 meta
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 確保一定要調用 next()
}
})
4、組件級
是指在組件內執行的鈎子函數,類似於組件內的生命周期,相當於為配置路由的組件添加的生命周期鈎子函數。鈎子函數按執行順序包括beforeRouteEnter、beforeRouteUpdate (2.2+)、beforeRouteLeave三個,執行位置如下:
<template>
...
</template>
export default{
data(){
//...
},
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調用
// 不!能!獲取組件實例 `this`
// 因為當守衛執行前,組件實例還沒被創建
},
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,但是該組件被復用時調用
// 舉例來說,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鈎子就會在這個情況下被調用。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用
// 可以訪問組件實例 `this`
}
}
<style>
...
</style>
beforeRouteEnter
:
路由進入之前調用,參數包括to,from,next。該鈎子在全局守衛beforeEach和獨享守衛beforeEnter之后,全局beforeResolve和全局afterEach之前調用,要注意的是該守衛內訪問不到組件的實例,也就是this為undefined,也就是他在beforeCreate生命周期前觸發。在這個鈎子函數中,可以通過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,並且把組件實例作為回調方法的參數,可以在這個守衛中請求服務端獲取數據,當成功獲取並能進入路由時,調用next並在回調中通過 vm訪問組件實例進行賦值等操作,(next中函數的調用在mounted之后:為了確保能對組件實例的完整訪問)。
beforeRouteEnter (to, from, next) {
// 這里還無法訪問到組件實例,this === undefined
next( vm => {
// 通過 `vm` 訪問組件實例
})
}
beforeRouteUpdate (v 2.2+)
:
在當前路由改變時,並且該組件被復用時調用,可以通過this訪問實例。參數包括to,from,next。可能有的同學會疑問,what is 路由改變 or what is 組件被復用?
對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,組件實例會被復用,該守衛會被調用
當前路由query變更時,該守衛會被調用
beforeRouteLeave
導航離開該組件的對應路由時調用,可以訪問組件實例this,參數包括to,from,next。
三、總結
全局路由鈎子:
beforeEach(to,from, next)、beforeResolve(to,from, next)、afterEach(to,from);
獨享路由鈎子:
beforeEnter(to,from, next);
組件內路由鈎子:
beforeRouteEnter(to,from, next)、beforeRouteUpdate(to,from, next)、beforeRouteLeave(to,from, next)
導航守衛回調參數
to:目標路由對象;
from:即將要離開的路由對象;
next:他是最重要的一個參數,他相當於佛珠的線,把一個一個珠子逐個串起來。以下注意點務必牢記:
1.但凡涉及到有next參數的鈎子,必須調用next() 才能繼續往下執行下一個鈎子,否則路由跳轉等會停止。
2.如果要中斷當前的導航要調用next(false)。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到from路由對應的地址。(主要用於登錄驗證不通過的處理)
3.當然next可以這樣使用,next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。意思是當前的導航被中斷,然后進行一個新的導航。可傳遞的參數與router.push中選項一致。
4.在beforeRouteEnter鈎子中next((vm)=>{})內接收的回調函數參數為當前組件的實例vm,這個回調函數在生命周期mounted之后調用,也就是,他是所有導航守衛和生命周期函數最后執行的那個鈎子。
5.next(error): (v2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
當點擊切換路由時:
beforeRouterLeave-->beforeEach-->beforeEnter-->beforeRouteEnter-->beforeResolve-->afterEach-->beforeCreate-->created-->beforeMount-->mounted-->beforeRouteEnter的next的回調
當路由更新時:
beforeRouteUpdate
官方文檔