在 vue 路由中,我们会用导航守卫控制页面的能否被进入查看,其中最后一步是调用 next() 函数,让中断的导航继续进行
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。所以确保要调用 next 方法,否则钩子就不会被 resolved
路由循环
关键点:
next () 可以传入参数,实现跳转到其他页面或者取消导航的功能。这时候要注意,不能堵住通道,要确保每一个条件最后都能调用没有传递参数的 next() 方法
。
否则,页面会陷入无限刷新的死循环。因为传入参数的 next() 方法会导致路由再次执行前置导航守卫,从而陷入循环。
例子1:验证 token ,失败则返回登录页
router.beforeEach((to,from,next) =>{
if (sessionStorage.getItem("token")) {
if(to.path === "/login"){
next({path:"/dashboard"})
}
else{
alert("1")
next()
}
}else{
next({path: "/login"}) // 会再次执行前置导航守卫,因为路径变化
}
})
上面的代码表面看没有问题:
如果 sessionStorage 有 token,并且即将要进入的目标路径是登陆页,就跳转到 /dashboard 页,如果是其它的页面,就直接进入
如果 sessionStorage 没有 token 就跳转到登陆页
但是代码执行会引起死循环,原因是没有出口,执行 next({path: "/login"}) 会再次执行全局前置导航守卫。进入 /login 页面前,就再次触发守卫,一直重复进入
代码改成下面的就正常了:
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem('token');
if (to.path != '/login' && !token) {
next({
path: '/login'
})
} else {
if (to.path == '/login' && token) {
next('/dashboard')
} else {
next()
}
}
})
例子2:动态添加路由
router.beforeEach((to, from, next) => {
function getRouteAndMenu () {
// 本地没有保存可访问路由,就需要计算
store.dispatch('d2admin/user/GenerateRoutes') // 获取可访问路由,在 vuex 中保存
router.addRoutes(store.state.d2admin.user.accessedRouters) // 和原有的固定路由合并到一起
const routeArray = routes.concat(store.state.d2admin.user.accessedRouters)
// 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', routeArray)
// 设置顶栏菜单
store.dispatch('d2admin/menu/GenerateHeaderMenu', {
role: store.state.d2admin.user.info.role,
menuHeader
})
// 设置侧边栏菜单
store.dispatch('d2admin/menu/GenerateMenu', {
role: store.state.d2admin.user.info.role,
menuAside
})
// 获取侧边栏菜单,在 vuex 中保存
store.dispatch('d2admin/menu/setMenuAside', {
menuAside
})
next({ ...to, replace: true }) // hack 以确保路由增加后,再进行跳转
}
if (from.name === null && to.name === '404') {
// 避免刷新出现 404 页面
getRouteAndMenu()
}
// 验证当前路由所有的匹配中是否需要有登录验证的
if (to.matched.some(r => r.meta.auth)) {
// 这里暂时将cookie里是否存有token作为验证是否登录的条件
// 请根据自身业务需要修改
const token = util.cookies.get('token')
if (token && token !== 'undefined') {
const accessedRouters = store.state.d2admin.user.accessedRouters
if (accessedRouters.length <= 0) {
getRouteAndMenu()
} else {
next()
}
} else {
// 没有登录的时候跳转到登录界面
// 携带上登陆成功之后需要跳转的页面完整路径
next({
name: 'login',
query: {
redirect: to.fullPath
}
})
}
} else {
// 不需要身份校验 直接通过
next()
}
})
上面的代码,主要是实现计算动态路由并添加到原有路由里、计算可展示的菜单栏,然后再跳转到相应页面的功能。
其中,next({ ...to, replace: true })
方法是一个 hack 方法,能确保路由添加了再进行跳转
问题在于,这里只将动态计算的路由保存到了内存,所以在页面刷新时,需要重新计算并添加,所以添加了下面的代码:
if (from.name === null && to.name === '404') {
// 避免刷新出现 404 页面
getRouteAndMenu()
}
而这会导致页面一直在重复执行 getRouteAndMenu() 操作,浏览器会重复导航进入当前刷新的页面,
原因:getRouteAndMenu 除了 hack 方法没有提供 next() 通道,每次 hack 都会再次执行导航守卫
解决方法:
不用 hack ,改成使用 async await ,按顺序执行,最后 next()
router.beforeEach((to, from, next) => {
async function getRouteAndMenu () {
// 本地没有保存可访问路由,就需要计算
await store.dispatch('d2admin/user/GenerateRoutes') // 获取可访问路由,在 vuex 中保存
router.addRoutes(store.state.d2admin.user.accessedRouters) // 和原有的固定路由合并到一起
const routeArray = routes.concat(store.state.d2admin.user.accessedRouters)
// 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', routeArray)
// 设置顶栏菜单
await store.dispatch('d2admin/menu/GenerateHeaderMenu', {
role: store.state.d2admin.user.info.role,
menuHeader
})
// 设置侧边栏菜单
await store.dispatch('d2admin/menu/GenerateMenu', {
role: store.state.d2admin.user.info.role,
menuAside
})
// 获取侧边栏菜单,在 vuex 中保存
await store.dispatch('d2admin/menu/setMenuAside', {
menuAside
})
next()
}
if (from.name === null && to.name === '404') {
// 避免刷新出现 404 页面
getRouteAndMenu()
}
// 验证当前路由所有的匹配中是否需要有登录验证的
if (to.matched.some(r => r.meta.auth)) {
// 这里暂时将cookie里是否存有token作为验证是否登录的条件
// 请根据自身业务需要修改
const token = util.cookies.get('token')
if (token && token !== 'undefined') {
const accessedRouters = store.state.d2admin.user.accessedRouters
if (accessedRouters.length <= 0) {
getRouteAndMenu()
} else {
next()
}
} else {
// 没有登录的时候跳转到登录界面
// 携带上登陆成功之后需要跳转的页面完整路径
next({
name: 'login',
query: {
redirect: to.fullPath
}
})
}
} else {
// 不需要身份校验 直接通过
next()
}
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。