ad+zuoye

### Vue.js 

## 一、 addRoutes权限控制

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

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

### 1.1 初始路由initRoutes

    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    //隐藏不需要渲染到页面上的路由
            }
        ];
   

### 1.2 动态路由 asyncRouetes

  //需要登陆后确认权限才能看到的路由
    var asyncRouetes = [
         {
             path:'/finance',
             component:Finance,
              meta: {
                 title: '财务信息',
                 roles: ['admin']
             }
            },
            {
            path:'/news',
            component:News,
            meta: {
                title: '新闻中心',
                roles: ['admin','guest']
              }
            }
        ];

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

const routerAll=new VueRouter({
            routes:initRoutes, 
            linkActiveClass:'active', //更新活动链接的class类名,默认的激活的 class
            linkExactActiveClass:'active-extact',  //精确激活的 class
            mode: "hash", //默认

        });

    在vue实例化的时候进行挂载

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

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


 ### 1.5 添加全局路由守卫

    // 导航守卫 -- 全局前置守卫
    routerAll.beforeEach((to,from,next)=>{
        //这里可以获取登陆后的权限, 如果是admin
    var auth = localStorage.getItem('userRole');
        asyncRouetes = asyncRouetes.filter((item,index)=>{
                return item.meta.roles.includes(auth)
        })
      //将新路由添加到路由中, 如果不加组件component不能正确渲染
    routerAll.addRoutes(asyncRouetes); 
       //为了正确渲染导航,将对应的新的路由添加到routerAll中
      routerAll.options.routes = [...initRoutes,...asyncRouetes];
          next();
    })

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

### 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实例对象

 ```javascript
  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里获取

 可以在计算器属性里设置
 ```javascript
 
    computed:{
          getMyRoutes(){
               var thisData = this.$router.options.routes;
              thisData = thisData.filter((option,index)=>{
                return !option.hidden
              })
                    
               return thisData;
            }
        },
 ```

 然后把 原来菜单部分 改成如下:

  <router-link tag='li' v-for="(item,index) in getMyRoutes" 
  :key="index" 
  :to="item.path">{{item.meta.title}}
  </router-link>



### 2.4 判断不要重复的添加动态路由

现在看似成功了, 但是可以优化下,不需要重复的对动态路由进行合并,如果已经添加 直接next就行

var allPaths = [];
asyncRouetes.forEach((option)=>{
    allPaths.push(option.path);
})

.....

 //需要判断下是否已经添加过动态路由,不要重复添加
        // 方式: 判断默认和路由和 读取的路由是否一致
       var isHAS =  initRoutes.some((option)=>{
           return allPaths.includes(option.path)
        });
        if(isHAS){
            next();
            return;
        }


### 2.5 添加注销功能
在App.vue里
<button type="button" @click="logOut()">注销</button>
methods:{
  ...
  logOut(){
      localStorage.clear();
      this.$router.push({
          path:'/login'
      })
  }
}

### 2.6 处理注销 与登录 后的动态菜单的实时更新 

更新路由实例上 的 options后 ,VueRouter.options.routes
如果才能在App.vue页面里的菜单也能实时的更新 

  2.6.1. 借助中心控制vue实例 , 利用eventEmitter
  2.6.2. 利用vuex

#### 2.6.1. 借助中心控制vue实例 , 利用eventEmitter
  window.EventEmitter = new Vue();

  在main.js的全局前置守卫添加 进行路由的 发布
  EventEmitter.$emit('allOption',VueRouter.options.routes);

  在App.vue里进行数据的订阅监听

  去掉计算器属性getMyRoutes, 在data变量里添加一个 变量getMyRoutes

  在生命周期函数 mounted里添加
   window.EventEmitter.$on('allOption',(res)=>{
        this.getMyRoutes = res;        
   })



## 二、 自定义指令的讲解

### 2.1 复习之前学的指令钩子函数

    /**
         * 自定义全局指令
         * 注:使用指令时必须在指名名称前加前缀v-,即v-指令名称
         */
            //钩子函数的参数  el,binding
        Vue.directive('hello',{
            bind(el,binding){ //常用!!    
                // console.log(el); //指令所绑定的元素,DOM对象
                el.style.color='red';
                console.log(binding); //name

                console.log('bind:指令第一次绑定到元素上时调用,只调用一次,可执行初始化操作');
            },
            inserted(el,binding){
                console.log(el)
                // binding.arg:传给指令的参数
                console.log('inserted:被绑定元素插入到DOM中时调用');
            },
            update(el,binding){
                console.log(el)
                console.log('update:被绑定元素所在模板更新时调用,模板还没更新完成');
            },
            componentUpdated(el,binding){
                console.log(el)
                console.log('componentUpdated:被绑定元素所在模板完成一次更新周期时调用');
            },
            unbind(el,binding){
                console.log('unbind:指令与元素解绑时调用,只调用一次');
            }
        });


### 2.2 封装弹框的函数以及拖拽的功能

```javascript
      function initPopHtml(title,html){  
            var popOut = document.getElementById('popOut')  ;
                if(popOut) {
                    return;
                }              
                    var popOut = document.createElement('div');
                    popOut.id='popOut';
                    popOut.className = 'pop-out';

                    var innerText = `
                        <div class="heaad-title">${title}</div>
                        <div class="pop-inner">${html}</div> 
                        <div class="footer"><button type="button" class="btn-close" id="btnClose">关闭</button></div>`;
                        popOut.innerHTML = innerText;

                        document.querySelector('body').appendChild(popOut);
                        toMoveDrag(popOut);
                        document.getElementById('btnClose').addEventListener('click',()=>{
                            popOut.remove();
                        });     
                }

            function toMoveDrag(boxDom){
                var moveFlag = false;
                var dis={};
                boxDom.querySelector('.heaad-title').onmousedown = function(e){
                    moveFlag=true; 
                    // 计算出鼠标落下点与边界的距离
                    dis.x = e.clientX - boxDom.offsetLeft;
                    dis.y = e.clientY - boxDom.offsetTop;
                }
                document.onmousemove = function(e){              
                    if (!moveFlag) {
                        return;
                    };
                    console.log(e.clientX);
                    // 根据拖拽距离设置当前拖拽元素的位置
                    boxDom.style.left = (e.clientX - dis.x) + 'px';
                    boxDom.style.top = (e.clientY - dis.y) + 'px';
                };
                document.onmouseup = function(e){
                    moveFlag=false; 
                }

       }
```

### 2.3 指令的编写

```javascript

  directives:{
        popwin:{
            bind(el,binding){
                el.onclick = function(){
                    // binding.value :列表的数据
                    
                    var data = binding.value;
                    var listUl = [];
                    listUl.push(`<ul class='user-list'>`)
                    data.forEach((item,index)=>{
                        console.log(item);
                        listUl.push(`<li><span>姓名:${item.name}</span><span>年龄:${item.age}</span></li>`)
                    })
                    listUl.push(`</ul>`);
                    initPopHtml(el.innerText,`<div class="content">${listUl.join('')}</div>`);
                }     
            },
            inserted(el,binding){
            },
            update(){
            },
            componentUpdated(){
            }
        }
    }

```

### 2.4 使用指令
 <button type="button" v-popwin="teacherlist">教师信息列表</button>
 <button type="button" v-popwin="studentlist">学生信息列表</button>

 因为需要传递数据, 所以需要在data 里添加 变量teacherlist,studentlist

        data(){
                return {
                    studentlist:[{
                        name:'song',
                        age:'10'
                    },{
                        name:'hong',
                        age:'8'
                    }],
                    teacherlist:[{
                        name:'孙老师',
                        age:'45'
                    },{
                        name:'刘老师',
                        age:'30'
                    }]
                }
            },

### 2.5 如果需要改变学生 以及 教师的信息
      <button type="button" @click="changeData()">添加学生信息列表</button>
        changeData(){
              this.studentlist.push({
                  name:'alice',
                  age:'10'
              })
          }



## 三、 自定义全局组件(插件)
 
    全局组件(插件):就是指可以在main.js中使用Vue.use()进行全局引入,然后在其他组件中就都可以使用了,如vue-router
        import VueRouter from 'vue-router'
        Vue.use(VueRouter);

    普通组件(插件):每次使用时都要引入,如axios
    import axios from 'axios'

## 插件, 全局组件, 局部组件的演示 
component-demo

vue init webpack-simple component-demo

### 第一种方式:插件
// 方式一: 插件通常用来为 Vue 添加全局功能 , 插件方式引入组件 
新建 components/user , 并添加index.js和 Login.vue

Login.vue
```javascript
<template>
    <div id="login">
        {{msg}}
    </div>
</template>

<script>
    export default {
        data(){
            return {
                msg:'用户登陆'
            }
        }
    }
</script>

<style scoped>
    #login{
        color:red;
        font-size:20px;
        text-shadow:2px 2px 5px #000;
    }
</style>
```

index.js
```javascript
import Login from './Login.vue'
export default {
    install:function(Vue){
        Vue.component('Login',Login);
    }
}
```
在main.js里引入并使用插件
main.js


import Login from '../components/user/index.js'
Vue.use(Login);

在vue页面中可以直接使用 ,如这里在 App.vue中使用

App.vue
```javascript
...
<h2>1. 插件方式引入</h2>
<Login ></Login>
....
```

## 第二种方式 :全局组件方式引入

main.js
在components里添加common文件夹 ,并添加info.vue  popwin.vue 
简单点写 info.vue popwin.vue 的内容

//  全局组件方式引入
// import commonInfo from '../components/common/info.vue'
// import commonPopwin from '../components/common/popwin.vue'
// 注册为全局组件
// Vue.component('commonInfo',commonInfo);
// Vue.component('commonPopwin',commonPopwin);

在页面中直接使用, 如这里在 App.vue中使用

App.vue
```javascript
...
    <h2>2. 全局组件方式引入</h2>
    <commonPopwin></commonPopwin>
    <commonInfo></commonInfo>
....
```

## 方式三: 如果项目插件比较多的时候,在components在common里新建index.js

index.js代码:
```javascript

import commonInfo from './info.vue'
import commonPopwin from './popwin.vue'

export default {
    commonInfo,
    commonPopwin
}

```

在main.js引入

main.js代码:
//通过components下common的index.js文件导入组件

// import commonComponents from '../components/common/index.js';
import commonComponents from '../components/common';
Object.keys(commonComponents).forEach((key) => {
    Vue.component(key, commonComponents[key])
});

在App.vue的全剧组件的使用方式同 方式二


##  局部组件方式引入


 在app.vue页面中 局部引入

 //局部引入组件
import myInfo from '../components/common/info.vue'

并在vue实例的选项添加 
components:{myInfo}

在template里 应用 
```javascript
<h2>3. 局部组件方式引入</h2>
<myInfo ></myInfo>
```













 

猜你喜欢

转载自www.cnblogs.com/laneyfu/p/12373254.html
ad