(精华)2020年7月14日 vue vue-router动态路由的实现权限控制

一、 addRoutes权限控制

场景: 对登陆成功后的用户可能会有不同的身份权限, 看到的系统菜单以及功能不一样, 这个时候需要用到 动态路由的方式来处理

路由结构:
|— initRoutes 默认是可以看到的路由,是所有用户都可以看到的路由菜单
|— asyncRouetes 需要登陆后确认权限才能看到的路由

1.1 初始路由initRoutes

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

import home from '../pages/home.vue';
import news from '../pages/news.vue';

var allRoutes = [{
    path:'/login',
    name:'login',
    meta:{
        title:'登陆'
    },
    component:()=>import('../pages/login.vue')
},{
    path:'/home',
    name:'home',
    component:home,
    meta:{
        title:'首页'
    },
},{
    path:'/news',
    name:'news',
    meta:{
        title:'新闻'
    },
    component:news
}]
export default new VueRouter({
    routes:allRoutes,
    mode:'hash', //history
    base:'/',
    //   vue-router 认为只有路由真正匹配时,才会加上 exact-active-link 这个 class,
    //   如果只有一部分重合,就会加上 active-menu。
    // fallback
    // 不是所有浏览器都支持前端路由的方式,如果不支持,设置 fallback: true,
    // vue 会自动 fallback 到 hash 模式。
    fallback: true,
    linkActiveClass: "active-menu",
    linkExactActiveClass: "exact-active-menu",
})
// 在main.js中把router 实例注入到 vue 根实例中,就可以使用路由了

1.2 动态路由 asyncRouetes

var asyncRouetes = [
    {
        path:'/finance',
        component:()=>import('../pages/finance.vue'),
        meta: {
            title: '财务信息',
            roles: ['admin']
        }
       },
       {
       path:'/staffs',
       component:()=>import('../pages/staffs.vue'),
       meta: {
           title: '员工信息',
           roles: ['admin','guest']
         }
       }
   ];

 export default  asyncRouetes;

1.3 默认在vueRouters 实例化的时候, 只是传入初始的路由

export default new VueRouter({
    routes:allRoutes,
    mode:'hash', //history
    base:'/',
    //   vue-router 认为只有路由真正匹配时,才会加上 exact-active-link 这个 class,
    //   如果只有一部分重合,就会加上 active-menu。
    // fallback
    // 不是所有浏览器都支持前端路由的方式,如果不支持,设置 fallback: true,
    // vue 会自动 fallback 到 hash 模式。
    fallback: true,
    linkActiveClass: "active-menu",
    linkExactActiveClass: "exact-active-menu",
})
// 在main.js中把router 实例注入到 vue 根实例中,就可以使用路由了
在vue实例化的时候进行挂载

1.4 进行登录处理,获取token和用户信息

 localStorage.setItem('token','XXXXXXXXXXX');
 localStorage.setItem('userRole','admin'); //submain, guest

1.5 添加全局路由守卫

import Vue from 'vue';

import axios from './providers/axios2.js';
import api from './providers/api.js';

import App from './App.vue'
import VueRouter from './router/index.js';

//如果全局, 别的页面都不需要做任何处理, babel-plugin-component也不需要配置
// import ElementUI from 'element-ui';
// import 'element-ui/lib/theme-chalk/index.css';
// Vue.use(ElementUI);

Vue.prototype.$axios = axios;
Vue.prototype.$api = api;

window.EventEmitter = new Vue();

//template模式
// new Vue({
//     el:'#app',
//     data:{
//         hello:'hello',
//         msg:'world'
//     },
//     // template:`<div id="app1">
//     //     <h1>{{msg}}</h1>
//     // </div>`,
//     components:{App}, //注册全局组件
//     template:'<App/>'
// });
import asyncRouetes from './router/dynamic.js';
var initRoutes = VueRouter.options.routes;

//优化
var allPaths = [];
asyncRouetes.forEach((option)=>{
    allPaths.push(option.path);
})
VueRouter.beforeEach((to, from, next) => {
    var userRole = localStorage.getItem('userRole');
    var token = localStorage.getItem('token');

     //需要判断下是否已经添加过动态路由,不要重复添加
     // 方式: 判断默认和路由和 读取的路由是否一致  
        var isHAS =  VueRouter.options.routes.some((option)=>{
            return allPaths.includes(option.path)
         });   
         if(isHAS){
             next();
             return;
         }
//判断是否存在token
    if(token && userRole){
        var asyncRouete =  asyncRouetes.filter((option,index)=>{
            return option.meta.roles.includes(userRole);
        });
        //将新路由添加到路由中, 如果不加组件component不能正确渲染
        VueRouter.addRoutes(asyncRouete);
         //为了正确渲染导航,将对应的新的路由添加到VueRouter中	
        VueRouter.options.routes = [...initRoutes,...asyncRouete];
        EventEmitter.$emit('allOption',VueRouter.options.routes)
        next();
    } else {
    // 跳转到登陆页面
        if(to.path=='/login') {
            next();
        } else {
            next('/login');
        }
    }
  
})

// render
var vm = new Vue({
    el:'#app',
    data:{
        hello:'hello',
        msg:'world'
    },
    router:VueRouter,
    // render(createElement){
    //     return createElement('div',{
    //         id:'app1'
    //     },[
    //         createElement('h1',this.msg)
    //     ])
    // },
    //使用组件,利用render函数渲染
    // render(h){
    //      return h(App)
    // },
    render:h=>h(App)

});

二、 权限控制在项目中的实际使用

2.1 配置必要的动态路由文件

在router文件夹下添加 dynamic.js
在src/pages文件夹下添加 finance.vue, staffs.vue作为测试

var asyncRouetes = [
    {
        path:'/finance',
        component:()=>import('../pages/finance.vue'),
        meta: {
            title: '财务信息',
            roles: ['admin']
        }
       },
       {
       path:'/staffs',
       component:()=>import('../pages/staffs.vue'),
       meta: {
           title: '员工信息',
           roles: ['admin','guest']
         }
       }
   ];
export default  asyncRouetes;

2.2 登录成功以后需要获取toekn以及用户信息

 localStorage.setItem('token','XXXXXXXXXXX');
 localStorage.setItem('userRole','admin'); //submain, guest

2.3 在入口文件main.js进行 导航守卫

引入文件:

 import asyncRouetes from './router/dynamic.js';

全局前置守卫配置:

注意:路由守卫的时候是针对vueRouter实例对象

 VueRouter.beforeEach((to,from,next)=>{
     //如果自定义了标题就取标题,否则拿全局标题
   window.document.title = to.meta.title?to.meta.title:'测试系统';

     //这里可以获取登陆后的权限
     var UserToken = localStorage.getItem('token');
     var userRole = localStorage.getItem('userRole');

     //判断是否存在token
     if(UserToken && userRole){
         //已登录
         var asyncRouteMenu = asyncRouetes.filter((item,index)=>{
             return item.meta.roles.includes(userRole)
         })

         //将新路由添加到路由中, 如果不加组件component不能正确渲染
         VueRouter.addRoutes(asyncRouteMenu); 
         //为了正确渲染导航,将对应的新的路由添加到VueRouter中
         
         var initRoutes = VueRouter.options.routes;
         VueRouter.options.routes = [...initRoutes,...asyncRouteMenu];
         next();

     } else {
         //是否处于登陆页面
         if(to.path=='/login'){ 
             //如果是登录页面路径,就直接next()
             next();
         } else {
             //不然就跳转到登录;
             next('/login');
         }
     } 
 })

2.3 如何处理菜单的显示

在App.vue里 ,原来的菜单部分

 <router-link to="/login" tag='li'>登陆</router-link> 
 <router-link to="/home?name=laney" tag='li'>主页</router-link>
 <router-link to="/news" tag='li'>新闻</router-link> 

需要修改为动态的, 因为这里所有的路由 不是写死的, 需要从路由实例this.$router.options里获取

可以在计算器属性里设置


  <!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>权限控制- addRoutes</title>
   <link rel="stylesheet" href="css/animate.css">
   <style>
   	.active{
   		font-size:20px;
   		color:#ff7300;
   		text-decoration:none;
   	}
       .main-menu a {
           display: inline-block;
           margin-right: 10px;
       }
   </style>
   <script src="js/vue.js"></script>
   <script src="js/vue-router.js"></script>
</head>
<body>
   <div id="itapp">
   	<div class="main-menu">
           <!-- 写成动态的 -->
           <!-- $router.options.routes  可以从计算器属性-->
           <!-- <router-link v-for="(item,index) in $router.options.routes" :key="index" :to="item.path">{{item.meta.title}}</router-link> -->
           <router-link v-for="(item,index) in getMyRoutes" :key="index" :to="item.path">{{item.meta.title}}</router-link>
           
       </div>
   	<div>
   		<transition enter-active-class="animated bounceInLeft" leave-active-class="animated bounceOutRight">
   			<router-view></router-view>
   		</transition>
   	</div>

   	<hr>
   	<button @click="push">添加路由</button>
   	<button @click="replace">替换路由</button>
   </div>

   <template id="user">
   	<div>
   		<h3>用户信息</h3>
   		<ul>
   			<router-link to="/user/login?name=tom&pwd=123" tag="li">用户登陆</router-link>
               <router-link to="/user/regist/alice/456" tag="li">用户注册</router-link>
   		</ul>
   		<router-view></router-view>
   	</div>
   </template>

   <script>
   	var Home={
   		template:'<h3>我是主页</h3>'
   	}
   	var User={
   		template:'#user'
   	}
   	var Login={
   		template:'<h4>用户登陆。。。获取参数:{{$route.query}},{{$route.path}}</h4>'
   	}
   	var Regist={
   		template:'<h4>用户注册。。。获取参数:{{$route.params}},{{$route.path}}</h4>'
       }
       var Finance={
   		template:'<h4>财务信息</h4>'
   	}
   	var News={
   		template:'<h3>我是新闻</h3>'
   	}
       //默认是可以看到的路由
   	var initRoutes=[
   		{
   			path:'/home',
   			component:Home,
   			// 路由元信息
               meta: {
                   title: '首页'
                }
   		},
   		{
   			path:'/user',
               component:User,
               meta: {
                   title: '用户'
                },
   			// children:[
   			// 	{
   			// 		path:'login',
               //         component:Login
   			// 	},
   			// 	{
   			// 		path:'regist/:username/:password',
               //         component:Regist
               //     }

   			// ]
           },
   		// {
   		// 	path:'*',
   		// 	redirect:'/home',
           //     hidden: true    //隐藏不需要渲染到页面上的路由
   		// }
   	];

   	 //需要登陆后确认权限才能看到的路由
   	 var asyncRouetes = [
        {
            path:'/finance',
            component:Finance,
             meta: {
                title: '财务信息',
                roles: ['admin']
            }
   		},
   		{
           path:'/news',
           component:News,
           meta: {
               title: '新闻中心',
               roles: ['admin','guest']
             }
           }
       ];
   	const routerAll=new VueRouter({
   		routes:initRoutes, //简写,相当于routes:routes
   		linkActiveClass:'active', //更新活动链接的class类名,默认的激活的 class
   		linkExactActiveClass:'active-extact',  //精确激活的 class
   		mode: "hash", //默认
   	});

   	导航守卫
   	//加入你获取了角色
   	routerAll.beforeEach((to, from, next) => {
   		// var auth = localStorage.getItem('userRole');
   		var auth = 'admin';
   		var asyncRouete =  asyncRouetes.filter((option,index)=>{
   			return option.meta.roles.includes(auth);
   		});
   		//将新路由添加到路由中, 如果不加组件component不能正确渲染
   		routerAll.addRoutes(asyncRouete);
   		 //为了正确渲染导航,将对应的新的路由添加到routerAll中	
   		routerAll.options.routes = [...initRoutes,...asyncRouete];
   		debugger
   		next();
   	})
   
   	new Vue({
           el:'#itapp',
   		router:routerAll, //注入路由
           computed:{
               getMyRoutes(){
                   var thisData = this.$router.options.routes;
                   return thisData;
               }
           },
   		methods:{
   			push(){
   				this.$router.push({path:'home'}); //添加路由,切换路由	
   			},
   			replace(){
   				routerAll.replace({path:'user'}); //替换路由,没有历史记录
   			}
           }
          
          
   	});
   </script>
</body>
</html>

借助中心控制vue实例 , 利用eventEmitter

  <template>
     <div id="app">
        <h1>{{msg}} <button type="button" @click="logOut()">注销</button></h1>
        <div >
            <ul class="main-menu">
             <router-link v-for="(item,index) in getMyRoutes" :key="index" :to="item.path" tag='li'>{{item.meta.title}}</router-link> 
                <!-- <router-link to="/login" tag='li'>登陆</router-link> 
                <router-link to="/home?name=laney" tag='li'>主页</router-link>
                <router-link to="/news" tag='li'>新闻</router-link>  -->
            </ul>
        <!-- <ul @click="gotoPage($event)">
            <li tag='home'>主页</li>
            <li tag='news'>新闻</li>
        </ul>    -->
        </div>
        <router-view></router-view>
    </div>
</template>

 <script>
    export default {
        name: 'app',
        data () {
            return {
                msg: 'Welcome to ruanmou',
                getMyRoutes:[]
            }
        },
        computed:{
            // getMyRoutes(){
            //     console.log('this.$router.options.routes')
            //     console.log(this.$router.options.routes)
            //     var thisData = this.$router.options.routes;
            //     return thisData;
            // }
        },
        methods:{
             logOut(){
                localStorage.clear();
                this.$router.push({
                    path:'/login'
                })
                location.reload();
              },
            gotoPage(ev){
                var target = ev.target,
                    tag = target.getAttribute('tag');
                switch(tag){
                    case 'home':
                        //相当于get方式
                        this.$router.push({
                            path:'/home',
                            query:{
                                name:'laney'
                            }
                        })
                    break;
                    case 'news':
                        this.$router.push({
                            path:'/news',
                            query:{
                                age:'10'
                            }
                        })
                    break;
                }
                }
        },
        mounted(){  
            EventEmitter.$on('allOption',(res)=>{
                console.log('mounted')
                console.log(res)
                this.getMyRoutes = res;
            })
            console.log(this.$router.options.routes)
        }
    }
</script>

<style scoped>
.main-menu li {
    display: inline-block;
    margin-right: 30px;
    background: #000;
    color: #fff;
    padding: 5px 20px;
    cursor: pointer;
}
.main-menu li.active-menu{
    background: #ff6600;
    
}
</style>

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页