Vue和Vue-Element-Admin(三):路由及constantRoutes和asyncRoutes的区别

Vue-Element-Admin的router总使用constantRoutes和asyncRoutes两种定义路由,在store.modules.permission.js中对于两者的权限有默认的判断方式,然后再根目录下的permission.js中进行导航守卫逻辑;

constantRoutes: 不需要动态判断权限的路由,如登录页、404、等通用页面;

asyncRoutes: 需动态判断权限并通过 addRoutes 动态添加的页面;

 一、vue-element-admin中动态路由实现

router文件夹下的index.js是项目总路由入口,index.js对模块注册后默认将constantRoutes无权限的路由增加进去,在store/modules/permission.js中维护静态+动态路由权限,并在根目录下的permission.js实现导航守卫逻辑,在/router/index.js中:

// router/index.js 部分代码

// 模块化,首先引入在modules目录下需要用的router
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
import yzgTest from './modules/yzg'

//constantRoutes 是静态路由,不需要动态权限判断
export const constantRoutes = []
//constantRoutes 是动态路由,需要动态权限判断
export const asyncRoutes = []

// 
const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router

在store/modules/permission.js中维持了一个全局state数组,维持当前登录用户所拥有的菜单权限,并且在路由守卫时候完成动态路由的切换,store/modules/permission.js中动态路由state状态的维护逻辑如下:

// 首先,从index.js中引入已经定义过的2个router数组
import { asyncRoutes, constantRoutes } from '@/router'

// 全局变量state,routes和addRoutes数组
const state = {
  routes: [],
  addRoutes: []
}

// mutations 是唯一可以更改state的函数,使用SET_ROUTES定义更改方法,SET_ROUTES(state, routes)的入参routes赋值给addRoutes数组,将constantRoutes静态路由数组增加routes;

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

// vue中store状态管理,通过actions调用 mutations 中封装的方法来实现对于state更改,
// 这里是vue-element-admin中动态路由的主要判断逻辑发生地方,首先判定用户角色是否包含admin(可能多角色),是则将所有asyncRoutes 加入到constantRoutes,若用户角色没有包含admin,则调用filterAsyncRoutes函数,递归地判定asyncRoutes.roles属性是否有该角色,即是否有权限,将有权限的router赋值accessedRoutes 后加入到constantRoutes;

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

//-----------------
// 两个上面使用的方法
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

export function filterAsyncRoutes(routes, roles) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })

  return res
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

 最后再看根目录下permission.js中路由守卫的逻辑,

// vue-element-admin中permission.js中导航守卫逻辑

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'

// 进度条
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// 白名单
const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist

// 路由之前,逻辑判定
router.beforeEach(async(to, from, next) => {
  // 开始进度条
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  // 有token,如果想去login则调到首页,如果是其他页面先判定是否有角色,有的话就跳过去,没有的话发请求得到永不信息,再调用函数维护store路由列表,报错要么没权限,要么是请求超时,就要返回error,清除token,返回登录页

// 没有token,且不是白名单内的,返回登录页
  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          const { roles } = await store.dispatch('user/getInfo')
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          router.addRoutes(accessRoutes)
          next({ ...to, replace: true })
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {

    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

// 进度条完毕
router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

 asyncRoutes中刻意使用meta.roles定义角色,因此在后端请求返回用户的角色后,在permission.js中能够挂的上判断逻辑;

二、vue中路由

vue-router是Vue.js官方的路由插件,和vue.js深度集成,用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。vue-router在实现单页面前端路由提供Hash模式和History两种模式,

router.push(location)=window.history.pushState
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL;

猜你喜欢

转载自blog.csdn.net/yezonggang/article/details/109809019