3

需求描述

最近接到一个新的需求,要求将系统的用户进行分类,用户登陆后根据不同的用户权限展示不同的功能列表。

这个功能在后台管理中很常见,大致的思路是

  1. 后台返回用户类型,前端根据用户类型生成该类用户可以访问的功能列表。
  2. 后台返回功能列表,前端进行循环渲染。

一个在前端生成功能列表,一个在后端返回,两个本质上类似,最终都是需要得到一个该用户的功能列表。但是两者都有一个不可忽视的东西,就是如果用户直接在地址栏输入会怎么样。

技术选型

由于公司项目不算小,为了后期维护方便,我还是选择了使用 vuex 完成上述的功能。

主要想法为在vuex中保存用户登陆后的状态,以及用户可访问的路由列表,这样的话,不涉及到父子组件间的数据传递,可以很方便的在单个组件中获取到用户的权限路由列表。

Vuex

如果只是想简单的使用一个vuex,了解state,mutation,action就足够你使用

在src文件夹下,创建一个store文件夹,如果项目简单,可以将state,mutations,actions,getters等写入到一个文件中

clipboard.png

主要代码很简单,只需要导入Vue,Vuex,并且调用Vue.use(Vuex)。

结合官方解释的个人理解,一个vuex文件就是一个仓库,它包含着你需要共享的变量、有关的事件、以及可以执行这些事件的行为,我们把这些导出去,在单个组件中引入,我们便可以在单个组件中对共享的变量进行改变。

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
    userLogin:false//共享变量
}
export default new Vuex.Store({
state
})

state

state主要功能是用来定义变量,代表你需要共享的一个状态。比如,我想要在每个组件中 共享用户的路由列表,所以,我需要先在state中定义一个存放路由列表的变量。

一、vuex文件代码
store/index.js中

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {    
    pressionList: [{ path: "/A", name: "页面A" }],//用户允许登陆的路由列表,为了方便记录,我写了一个默认值,这个可以不用
    userLogin:false//用户登陆状态 
}
export default new Vuex.Store({
state
})

二、main.js文件代码
main.js中直接引入 store文件,并进行全局注册即可。这样在每个组件中你都可以调用state里的共享变量啦。

import Vuex from 'vuex'
import store from './store/index/'
Vue.user(Vuex)
new Vue({
    el: '#app',
    router,
    store,
    components: { App },
    template: '<App/>'
})

那怎么样在组件中读出state的变量呢?有两种方法:直接获取、或者使用mapState辅助工具。


插播一条题外话
我们做为一枚程序猿,必须要试着去读API,一次读不懂就多读几次。再读不懂请教别人,再去读一次,你会发现每次读都有不同的收获。

以下为vuex官方API,我会试着带一点API里的东西讨论这次需求的生成。
https://vuex.vuejs.org/zh/guide/


三、单个组件中读取state
以下为官方API提供对state用法的描述。
clipboard.png

上半段,将状态从根组件“注入”到每一个子组件,我们已经在main.js里完成了。
主要看下半段,在computed属性里,监听state中的变量,然后通过this.$store.state获取

套入到我们的需求中,我们需要获取用户的路由列表,以及登陆状态

3.1 直接获取

 computed: {
    pressionList(){
      return this.$store.state.pressionList
    },
    userLogin(state){
      return state.userLogin//或者接收一个state类型的变量,直接调用state里的共享属性
    }
  }

此时我们已经获取到了state里的状态,此时它就像一个data里定义的变量,我们可以使用数据双向绑定的形式调用它。如:我想要输出用户登陆的状态。或者在事件中展示这个状态。

<div>{{userLogin}}</div>

methods:{
    login(){
        console.log(this.userLogin)
    }
}

3.2 借助mapState辅助工具获取
假如说state里保存了很多个共享的状态,我们都需要怎么办呢,这样一个一个写太复杂,这时候,mapState就帮了我们很大的忙。
还是先看官方上的文章解释
clipboard.png
我们需要在组件的内部导入mapState,然后在computed属性里监听,以上图片无非就是说了几种情况

  • 如果你只是想单纯的使用state里的变量,那你用箭头函数的形式,会更加简单
  • countAlias:'count'//相当于给state里的count变量起了一个别名叫countAlias,调用时使用countAlias
  • 如果你需要在共享变量里添加一个其它的变量作为返回结果时,这时不能够使用箭头函数,而是常规的函数才能操作。因为箭头函数本身是没有this这个东西哒。
  • 如果state中有很多的变量,而且,你也不打算给它们起别名,或者做其它的操作,你可以使用数组的形式。向state传递一个字符串的数组,state会自己一一的对应。
    mapState(['pressionList','userLogin'])

clipboard.png是数组,所以中间是 [],不是{}

mutation

有了以上的基础,我们可以获取到一个共享的状态了,但是如果我们需要在共享的状态上做点小动作怎么办呢?
mutation就帮我们做这些事件,我们需要把对共享状态需要做的事件写到mutation中,然后在组件中调用mutation里的事件,就可以了。

同样的,可以去官网看一下关于mutation的介绍。这里就不放图了,直接上代码
一、定义mutation
store/index.js

const mutations = {
 changePressionList(state,list){
    state.pressionList = list;
 }
}

export default new Vuex.Store({
    state,
    mutations//同样就
})

mutation里的事件第一个参数肯定是state,用来调用共享的所有变量,后面可以接收一个其它的参数。
这里我是需要改变 共享路由的值,所以我需要接收到一个新的值来替换掉旧的值

个人理解时间
mutation是用来定义一个事件的,我们需要对共享变量做的一系列的动作。但是,这个事件自己是不会去执行的,像methods里定义的一个事件一样,你需要去触发它。而mutation触发的方法就是用commit。如你想单击的时候触发mutation事件,你需要在单击事件里使用commit调用 mutation事件。

二、调用mutation
同样两种方式,直接调用、借助mapMutations辅助函数
2.1 直接调用
如果你理解了获取state变量,这个mutation调用会简单很多。
既然mutation是一个事件,所以,我们需要在methods里去写

<button @click="changePress">更改用户路由</button>

methods:{
 changePress(){
      let B= [{ path: "/B", name: "页面B" }];
      this.$store.commit('changePressionList',B)
 }
}

因为我的mutation定义的事件里接收了一个参数,那我调用的时候就需要传递一个参数。
commit 第一个参数是你需要调用的mutation事件,第二个是mutation事件里需要的一些参数。
点击页面上的改变按钮,你会发现输出的pressionList变量会发生改变。

我这个只是简单的用法,还有更深层次的,想学习的可以去官网查看

2.2 借助mapMutations辅助函数

clipboard.png

也是三种情况,你可以以字符串数组的形式调用mutation里定义的事件,也可以给事件起一个别名。这个也跟state类似

  • 事件不需要起别名,事件名与mutation名一致,并且没有接收变量这种情况下,可以使用
    ...mapMutations(['changePressionList'])
    注意,这里是[],以字符串数组的形式调用
  • 在需要传值的情况下,也可以使用字符串数组的形式传递。但是调用时,需要传递相应的参数。

    <button @click="pressionList(list)">改变</button>
    data(){
        return {
            list:[{ path: "/B", name: "页面B" }]
        }
    }
    methods:...mapMutations([
        'pressionList'
    ])
  • add:'increment' 相当于给mutation中定义的increment事件起了一个add别名,在组件中调用时,直接调用 add即可。

action

有了state、mutation的基础,我们就可以实现我们的第一个功能。在用户登陆成功后,修改userLogin的状态,然后将用户的路由列表写入。

一、定义action
action与mutation类似,但是他是用来提交mutation的。相当于我们在action里提交mutation,然后我们再调用action。
action接收到的第一个参数,是一个与store对象有相同的方法和属性的context对象,包括commit,state,getters方法等等。
利用解构赋值,我们也可以只用context其中的一个方法,如commit,所以我们会经常的看到({commit,state})样式的代码
如果需要传递参数,可以在后面添加

const actions = {
    changePress({commit},list){
        commit('changePressionList',list)
    }
}

二、调用action
与muation不同的是,调用action中的事件使用的是dispatch,意思是分发mutation中的事件。
调用方法也是在methods中,分为两种,与mutation用法一致。不再赘述

路由限制

我们根据以上的做法,我们得到了一个有效的路由列表。我们可以在首页里循环这个列表,有哪个路由我们就渲染哪个路由
底部路由代码

clipboard.png
即使如此,如果有人直接在地址栏里输入了他不能访问的路由,我们该怎么办呢?
这里就需要两个知识

  • 动态添加路由
  • 改变路由文件

动态添加路由欠着吧~~

改变路由文件很简单,只需要在路由文件末尾添加一个匹配所有路由的
path为/* 表示,当用户在地址栏直接 输入一个错误的地址时,如路由文件中并没有abcd这个页面,但是用户在地址栏输入了/abcd,这时会自动跳到登陆页面。

export default new Router({
    routes: [{
            path: '/',
            name: 'Login',
            component: Login
        },
        {
            path: '/Father',
            name: 'Father',
            component: Father
        },
        {
            path: '/Child',
            name: 'Child',
            component: Child
        },
        {
            path: '/Home',
            name: 'Home',
            component: Home
        },
        {
            path: '/A',
            name: 'A',
            component: A
        },
        {
            path: '/*',
            name: 'Login',
            component: Login
        }
    ],
    mode: 'history'
}, )

个人公众号

对我感兴趣的盆友,也可以关注我的个人公众号---小嘀咕影院官网。会不定期更新技术知识,旅行游记以及影视推荐

clipboard.png


麦篱落
146 声望4 粉丝