vue-router + vuex 做动态路由

前几天做vue-router的动态路由做的焦头烂额,最后还是解决了,故记录下来。

我这边通过后端接口给过来的菜单+角色来判断用户的路由添加,我先定义两种路由:需要权限校验的路由和基础路由

import layout from '@/layout/layout'

/**
 * 需要权限校验的路由
 */

export const  authRouters = [
    {
        path: '/layout',
        name: 'layout',
        component: layout,
        redirect :'',
        meta: {
        },
        children: [
            {
                path: '/detail',
                name: 'detail',
                meta: { 
                    role: ['admin','project']
                },
                component: () => import('@/pages/detail')
            },
            {
                path: '/list',
                name: 'list',
                meta: {
                    role: ['admin',]
                },
                component: () => import('@/pages/list')
            }
        ]
    },
    // 404 注意:使用动态路由时,这个404跳转的路由一定要放在最后添加,不然会无限跳转到404
    {
        path: '*', redirect: '/404', hidden: true
    }
]

/**
 * 基础路由
 */
export const constantRouters = [
    {
        path: '/',
        redirect: '/login'
    },
    {
        path: '/login',
        name: 'login',
        component: () => import('@/pages/login')
    },
    //这里才是定义404页面的地方
    {
        path: '/404',
        name: '404',
        component: () => import('@/pages/error-page-404')
    }
]

 然后在vuex 定义一个action 用于装配路由: /store/permission.js

import {constantRouters, authRouters} from '@/router/routes'
import { cloneDeep } from 'lodash'

/**
 * 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
 */
function hasPermission(roles, route) {
    if (route.meta && route.meta.role) {
        let flag = -1
        for (let i = 0, len = roles.length; i < len; i++) {
            flag = route.meta.role.indexOf(roles[i])
            if (flag >= 0) {
                return true
            }
        }
        return false
    }
    return true
}

/**
 * 通过角色过滤路由
 */
function filterAsyncRouter(routerMap, roles) {
    const accessedRouters = routerMap.filter(route => {
        if (hasPermission(roles, route)) {
            if (route.children && route.children.length) {
                route.children = filterAsyncRouter(route.children, roles)
                //默认指向第一个子路由
                if(route.children.length > 0){
                    route.redirect = route.children[0].path
                }
            }
            return true
        }
        return false
    })
    return accessedRouters
}

export default {
    namespaced: true,
    state: {
        routers: constantRouters,
        addRouters: [],
        roles:[],
        //用于判断用户是不是刷新过
        routesAdded:false
    },
    mutations: {
        SET_ROUTERS: (state, data) => {
            state.addRouters = data;
            state.routers = constantRouters.concat(data);
            //路由加载好后将标识设置一下
            state.routesAdded = true;
            console.log('-----mutations---SET_ROUTERS----', data)
        },
        SET_ROLES: (state, data) => {
            state.roles = data;
            console.log('-----mutations---SET_ROLES----', data)
        },
    },
    actions: {
        /**
         * @description 装配路由 
         */
        configRouter({ commit },userInfo){
            return new Promise(resolve => {
                let roles = ['admin'] //用户的角色列表,后端给或者自己逻辑判断都可以
                //使用lodash 深拷贝方法,防止在验证过程中改变了 定义好的路由
                const appRouter = filterAsyncRouter(cloneDeep(authRouters),roles)
                commit('SET_ROUTERS', appRouter);
                commit('SET_ROLES', roles);
                // end
                resolve(appRouter)
            })
        }
}

重点来了,我们会发现页面每次刷新都跳到404页面,那是因为刷新使整个页面状态重置了包括vue-router、vuex等。

我们可以在vue-router的前置守卫中加入一些逻辑来阻止这种问题发生 /router/index.js :

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '@/store/index'
import util from '@/libs/util.js'

// 路由数据
import { constantRouters }  from './routes'

Vue.use(VueRouter)

// 导出路由 在 main.js 里使用
const router = new VueRouter({
    mode: 'history',
    routes: constantRouters
})

/**
 * 路由拦截
 * 权限验证
 */
router.beforeEach((to, from, next) => {
    // 这里暂时将cookie里是否存有token作为验证是否登录的条件
    if (util.cookies.get('token')) {
        // 动态添加可访问路由表
        let routesAdded = store.getters.routesAdded;
        //判断用户是否刷新了页面
        if(routesAdded){
            if(to.path === '/login'){
                next({ path: '/dashboard' });
            }
            next();
        }else{
            //获取用户信息
            store.dispatch('/user/load').then(()=>{
                //重新生成路由并装载
              store.dispatch('/permission/configRouter',store.getters.userInfo).then(()=>{
                    router.addRoutes(store.getters.addRouters);
                    const redirect = decodeURIComponent(from.query.redirect || to.path)
                    if (to.path === redirect) {
                        // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
                        next({ ...to, replace: true })
                    } else {
                        // 跳转到目的路由
                        next({ path: redirect})
                    }
                })
            }).catch((e) =>{
                next({ path: '/' });
            })
        }
    }else{
        if(to.path !== '/login'){
            next({ path: '/' });
        }
        next();
    }
});

router.afterEach(to => {
  // 更改标题
  util.title(to.meta.title)
})

export default router

以上的思路就是每次路由跳转的时候就做一次判断,先判断用户登录的标示,再我们设置在vuex里的变量有没有被重置,以此来判断需不需要重新装载路由。当然这个逻辑只是一个初步的跑的通的例子,后面需要再加上一些判断逻辑也行。

 

===================================分割线-=======================================

后面实践发现,退出登录并没有做路由的清空,导致后面用户居然有前面用户的路由,这就很bug了,解决方法是每次登出,将路由重置:

//创建路由方法
const createRouter = () => new VueRouter({
    mode: 'history',
    routes: constantRouters
})

//初始化路由
const router = createRouter();

//重置路由:登出时调用
export function resetRouter() {
    const newRouter = createRouter()
    router.matcher = newRouter.matcher // the relevant part
}

export default router

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值