在 vue 路由中,咱们会用导航守卫控制页面的可否被进入查看,其中最后一步是调用 next() 函数,让中断的导航继续进行vue
当一个导航触发时,全局前置守卫按照建立顺序调用。守卫是异步解析执行,此时导航在全部守卫 resolve 完以前一直处于 等待中。因此确保要调用 next 方法,不然钩子就不会被 resolved
关键点:vuex
next () 能够传入参数,实现跳转到其余页面或者取消导航的功能。这时候要注意,不能堵住通道,要确保每个条件最后都能调用没有传递参数的 next() 方法
。
不然,页面会陷入无限刷新的死循环。由于传入参数的 next() 方法会致使路由再次执行前置导航守卫,从而陷入循环。浏览器
例子1:验证 token ,失败则返回登陆页cookie
router.beforeEach((to,from,next) =>{ if (sessionStorage.getItem("token")) { if(to.path === "/login"){ next({path:"/dashboard"}) } else{ alert("1") next() } }else{ next({path: "/login"}) // 会再次执行前置导航守卫,由于路径变化 } })
上面的代码表面看没有问题:session
若是 sessionStorage 有 token,而且即将要进入的目标路径是登录页,就跳转到 /dashboard 页,若是是其它的页面,就直接进入异步
若是 sessionStorage 没有 token 就跳转到登录页async
可是代码执行会引发死循环,缘由是没有出口,执行 next({path: "/login"}) 会再次执行全局前置导航守卫。进入 /login 页面前,就再次触发守卫,一直重复进入ide
代码改为下面的就正常了:函数
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:动态添加路由spa
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() } })