1. 需求描述
最近开发的一个项目,涉及到这样一个需求:随着项目的不断推进,后台管理功能逐渐增多,与此同时,静态路由表也逐渐扩大,需要把静态路由方式转换为动态路由方式。要完成这样一个转换,有几个技术要点需要解决,其中一个就是前端的实现方式。那么,前端如何实现呢?
2. 实现
Vue动态路由的前端实现,网上有不少参考资料。但大多存在代码冗余,思路模糊不清的情况。现在整理一下思路。当前端发送登录请求login向后台请求数据后,后台会返回一个user对象至前端,前端把该对象保存至vuex中,同时,保存user中的token对象至vuex中。在vuex模块对应的store文件家中,Vue2对应两个文件:getters和index。
getters 文件源码
let getters = {
user: state => state.user,
token: state => state.token,
routes: state => state.routes
}
export default getters
index文件源码
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import persistedState from 'vuex-persistedstate'
Vue.use(Vuex)
const state = {
token: '',
user: '',
routes: []
}
const mutations = {
setToken (state, token) {
state.token = token
},
setUser (state, user) {
state.user = user
},
setRouter (state, routes) {
state.routes = routes
},
clear (state) {
state.token = ''
state.user = ''
state.routes = []
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
plugins: [persistedState({ storage: window.sessionStorage })]
})
export default store
注意: 其中persistedState 为持久化插件,使得刷新的时候,保存vuex中的数据不丢失,从而回到当前页面,而不至于刷新后丢失数据后回到登录页。
回到刚才的话题,在前端登录请求中,登录认证成功后,在回调函数中,会获取到user对象。该对象中保存有token信息,同时,也保存有treeMenu信息。
如何在静态路由和动态路由之间比较自由的切换,显然,修改的地方越少越好。
由于router文件夹中的静态路由涉及到权限敏感的路由数据会被注释掉,并放入后台数据库,此为需要修改的第一个地方。即 注释和反注释。
需要特别说明的是,在登录方法中,并不需要修改代码。
需要修改代码的第二个地方,就是路由守卫的文件里面。
源码如下所示
import router from './router'
import store from './store'
import {deepClone} from './utils'
// 获取组件的方法
let _import = file => require('@/views' + file + '.vue').default
// 页面全局路由列表
let getRouter = []
// 默认静态路由的长度
let staticRouterLength = 3
let staticRouter = []
router.beforeEach((to, from, next) => {
// 判断该路由是否需要登录权限
if (to.path === '/' || to.path === '/app2' || to.path === '/login') {
next()
} else {
if (!store.state.token) { // 未登录
next({
path: '/login'
// 将跳转的路由path作为参数,登录成功后跳转到该路由。
// query: {redirect: to.fullPath}
})
} else {
// 已登录
// next()
// ------------切换为动态路由----------------
// 已登录的情况下,从vuex中获取用户菜单列表,进行组件的初始化。
// 静态路由为登录等基本的路由。
if (store.state.routes.length === 0 || router.options.routes.length === staticRouterLength) {
if (router.options.routes.length === staticRouterLength) {
// 静态路由的情况下,把静态路由信息存入静态变量
staticRouter = deepClone(router.options.routes)
}
// 从vuex的user对象中获取菜单树后台数据
let treeMenu = deepClone(store.state.user['treeMenu'])
// 通过页面全局变量,进一步加载getRouter
getRouter = filterAsyncRouter(treeMenu)
// 把该加载的全局变量存入vuex中
store.commit('setRouter', getRouter)
// 跳转到异步加载路由
nextAsyncRouter(to, next)
} else {
next()
}
}
}
})
function nextAsyncRouter (to, next) {
// 更新路由数据 : 静态路由数据 + 动态路由数据
router.options.routes = deepClone(staticRouter).concat(getRouter)
router.addRoutes(getRouter)
// 跳转到目标位置,循环加载,直到组件初始化好。
next(to, {replace: true})
}
/**
* 递归加载路由组件
* @param asyncRouterMap 可获取的路由列表
* @returns {*}
*/
function filterAsyncRouter (asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
route.component = _import(route.component)
if (route.children && route.children.length) {
// 递归调用
route.children = filterAsyncRouter(route.children)
}
}
return true
})
return accessedRouters
}
注意:上述代码中,静态路由和动态路由的切换很简单,只需要else中,已登录的情况下,静态路由变为next(),把其他代码注释即可。
上述代码,实现了静态路由表和动态路由表的拼接,即基本的一些功能可以放在前端静态路由中,涉及到用户角色权限的路由部分,放在数据库中,根据用户的权限,向数据库请求对应的动态菜单返回加载即可。
干货收集分享:
下面,附上之前收集整理并若干修改之后的工具代码的两个方法。
/**
* 深度拷贝
*/
export function deepClone (obj) {
/**
* 加入空值判断
*/
if (obj === null) {
return null
}
let newObj = obj instanceof Array ? [] : {}
for (let k in obj) {
newObj[k] = typeof obj[k] === 'object' ? deepClone(obj[k]) : obj[k]
}
return newObj
}
/**
* 判断对象是否是数组
*/
export function isArrayFn (value) {
// 首先判断浏览器是否支持Array.isArray这个方法
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
} else {
return Object.prototype.toString.call(value) === '[object Array]'
// return obj.__proto__ === Array.prototype;
}
}
3. 结语
Vue动态路由涉及到的知识从动态组件的加载,到后台,再到前端,每一个地方都有若干需要注意的细节要点,可以说,实现这个功能,参考了不少网络资料,从中获益匪浅。在Vue动态路由实现的参考文章中,差不多都会在路由守卫中重新发送请求,获取treeMenu数据,其实,这个地方是没有必要再次发送请求的,因为在login方法中,已经把user对象存入vuex中,该user对象中,已经包含了完整的treeMenu信息。 只是在动态加载组件的时候,因为是按需异步加载,如果加载没有完成,需要从新跳转到目标位置,循环加载,直到组件初始化好(参考上面源码注释即可理解)。
4. 参考资源
(1)vue 实现动态路由
(2)关于vue-router动态添加路由$router.options不更新的解决办法
(3)js如何判断一个对象是数组(函数)