today

### 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'               })           }
## 三、 Vuex
### 1. 简介     Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。     简单来说,用来集中管理数据,类似于React中的Redux,都是基于Flux的前端状态管理框架    
    store里面有4个核心内容,     State、 是存数据用的     Getters、 有点类似计算属性,进行简单的逻辑计算      Mutations、  是一些简单的方法,又来改变state     Actions 、 Actions是较为复杂的方法,并不能直接改变状态,而是提交mutations,可以包含任意异步操作。
    官方的意图:     1. 不要直接修改state     2. 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation      ### 2. 基本用法 创建项目  vue init webpack-simple vuex-demo
#### 2.1 安装vuex     npm install vuex -S   或者 yarn add vuex
#### 2.2 创建store.js文件,在main.js中导入并配置store选项     Vuex的核心是Store(仓库),相当于是一个容器,一个store实例中包含以下属性的方法:         state       定义属性(状态、数据)         getters     用来获取属性         actions     定义方法(动作)         commit      提交变化,修改数据的唯一方式就是显式的提交mutations         mutations   定义变化         注:不能直接修改数据,必须显式提交变化,目的是为了追踪到状态的变化 
store.js ```javascript import Vue from 'vue' import Vuex from 'vuex'
Vue.use(Vuex); //定义属性(数据) var state={     count:6 }
//创建store对象 const store=new Vuex.Store({     state }) //导出store对象 export default store; ```
main.js ```javascript
import store from './store.js' //导入store对象
new Vue({   store,    //配置store选项,指定为store对象,会自动将store对象注入到所有子组件中, // 在子组件中通过this.$store访问该store对象   el: '#app',   render: h => h(App) })
```
在App.vue中添加:
 获取数据: ```javascript   <h1>{{ msg }}</h1>     <h2>获取store仓库中的所有数据state</h2>    {{$store.state}}
  <h2>从计算属性中获取:</h2>    {{getCount}}  <br/>    基数还是偶数: {{isEvenOrOdd}}

  computed:{     getCount:function(){         return this.$store.state.count     },     isEvenOrOdd(){       return this.$store.state.count%2==0?'偶数':'奇数';     }   }
```

如果有多个页面 获取 state里的数据,进行了一些简单的业务逻辑处理,可以借助store里的getters
从store的getters获取 {{$store.getters.isEvenOrOdd}} ```javascript
//定义getters var getters={     count(state){         return state.count;     },     isEvenOrOdd(state){         return state.count%2==0?'偶数':'奇数';     } } ```
const store=new Vuex.Store({     state,     getters })
#### 2.4 编辑App.vue             在子组件中访问store对象的两种方式:         方式1:直接通过this.$store访问         方式2:通过计算属性(相对于state数据变换后的)         方式3:通过在store里定义getters,相当于把计算属性里的计算方式拿到getters里封装,然后其他页面直接获取         方式4: 通过辅助函数mapState  : 获取state          方式5: 通过辅助函数mapGetters  :获取getters

#### 2.5 如何改变数据
        在子组件中改变store对象里state的两种方式:         方式一:通过commit一个mutations         方式二:通过dispatch一个mutations          方式三:通过mapMutations、mapActions访问,vuex提供了两个方法:             mapMutations  获取mapMutations             mapActions    获取mapActions
##### 2.5.1 通过commit一个mutations
在store.js里添加
//定义mutations,处理状态(数据)的改变 const mutations={         // 没有参数的情况     // increment(state){     //  state.count++;     // },
    // 有参数的情况     increment(state,payload){           state.count +=payload.data;     },
    decrement(state,payload){         state.count -=payload.data;         // state.count--;     } }
const store=new Vuex.Store({     state,     getters,     mutations })
在app.vue里 改变state
    <button @click="increment">增加</button>     <button @click="decrement">减小</button>     <button @click="incrementAsync">增加</button>
 methods:{     increment(){           this.$store.dispatch({             type:'increment',             data:6         })     },     decrement(){       this.$store.dispatch({             type:'decrement',             data:5         })     },     incrementAsync(){         this.$store.dispatch({             type:'incrementAsync',             data:5         })     }   },   mounted(){         // 改变数据         // 1. 直接改state  .不推荐         this.$store.state.count = 80; //官方不推荐
        // 2.通过仓库管理员mutation -  用来修改数据           this.$store.commit('increment',{             data:6         })         this.$store.commit({             type:'increment',             data:5         })
   } ##### 2.5.1 通过dispatch一个mutations  //定义actions,要执行的操作,如流程判断、异步请求等 const actions = {
    // increment(context,payload){//包含:commit、dispatch、state     //     console.log(context);     //  context.commit('increment',payload)     // },          increment({commit},payload){         commit('increment',payload); //提交一个名为increment的变化,名称可自定义,可以认为是类型名     },          decrement({commit,state},payload){         if(state.count>10){             commit('decrement',payload);         }     },     incrementAsync({commit,state},payload){         //异步操作         var p=new Promise((resolve,reject) => {             setTimeout(() => {                 resolve();             },3000);         });
        p.then(() => {             commit('increment',payload);         }).catch(() => {             console.log('异步操作');         });     } }
const store=new Vuex.Store({     state,     getters,     mutations,     actions })
在app.vue页面
  // 3. 通过仓库总监  actions 发送请求     // this.$store.dispatch('increment',{     //     data:6     // });     this.$store.dispatch({           type:'increment',           data:60       })

### 3. 分模块组织Vuex          
    |-src         |-store             |-index.js             |-getters.js             |-actions.js             |-mutations.js             |-modules  //分为多个模块,每个模块都可以拥有自己的state、getters、actions、mutations                 |-user.js                 |-cart.js                 |-goods.js                 |....
上一篇:java8时间新特性


下一篇:LocalDate的使用