(精華)2020年7月14日 vue vue-router動態路由的實現許可權控制

愚公搬程式碼發表於2020-07-14

一、 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>

相關文章