Vuex入门

什么是Vuex

Vue.js官网中是这样描述的:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

总结一下就是:Vuex是Vue中的状态管理工具

为什么要使用Vuex

Vuex使工程的各种状态的可控性得以提高。
比如:
parent.vue组件里面有两个子组件:borther.vue和sister.vue,parent和brother或者和sister通信都简单,props,emit就好了,但是如果brother和sister需要通信的话怎么办呢?

如果不用Vuex的话可以想到,brother跟parent说,我想和妹妹说话,你帮我把这句话带给妹妹吧,妹妹想跟哥哥说话的时候也需要让parent给帮忙传递,很明显,这样是不太好控制的。

如果我们用了Vuex就会是这样的:
brother想跟sister说句话,提交到store里面,sister直接就可以监测到。

引入Vuex

CDN/直接下载

直接引入vue.js 和 vuex.js文件就可以

npm
npm install vuex --save

项目中新建store文件,创建一个store实例:

export default new Vuex.Store({
    state: {
        talkMsg: []
    },
    mutations: {
        updateTalk (state,msg) {
          state.talkMsg.push(msg);
        }
    }
});

组件中引入store,可以通过 this.$store.state.talkMsg 获取state中的数据,通过this.$store.commit('updateTalk',data)向store中提交数据

Vuex的核心

vuex的核心概念有:state,getter,mutation,action,module。

state

state存放项目中所有的公共状态,相当于vue中的data,
在组件中访问state有两种方式:

  1. 在组件的计算属性中返回状态:
computed:{
   talkMsg(){
       return store.state.talkMsg;
   },
 },

这种方式是最简单的方式,但是这种方式在模块化的构建系统中需要state的组件要频繁导入,并且在测试组件是需要模拟状态。

  1. 在组件中引入通过store选项将状态注入组件中,调用方法:
export default {
  name: 'parent',
  store,
  data () {
    return {
        brotherTalk:'',
    }
  },
  computed:{
    talkMsg(){
        return this.$store.state.talkMsg;
    },
  },
};

getter

getter相当于store的计算属性,定义方式如下:

getters: {
    talkMinLength3: state => {
        return state.talkMsg.filter(msg => msg.msg.length>3);
    }
},

在组件中可以随意使用,使用方式:

this.$store.getters.talkMinLength3

如果需要传参:

getters: {
        talkMinLength3: state =>(length) => {
            return state.talkMsg.filter(msg => msg.msg.length>length);
        }
    },

使用:

this.$store.getters.talkMinLength3(2)

mutation

  1. 相当于vue中的methods,用于更改store中的状态,state作为第一个参数传入,也可以传入额外参数,
  2. 不能在组件中直接调用,只能commit,
store.js
mutations: {
    updateTalk (state,msg) {
      state.talkMsg.push(msg);
    }
}

brother.vue
this.$store.commit('updateTalk',parme);
  1. 也可以以对象的方式提交:
brother.vue
this.$store.commit('updateTalk',{
    user:'brother',
    msg:'hello world'
});
或者:
this.$store.commit({
    type:'updateTalk',
    user:'brother',
    msg:'hello world'
});

store.js
mutations: {
    updateTalk (state,payload) {
      state.talkMsg.push(payload);
    }
}
  1. Mutations必须是同步函数

Action

Action 类似于Mutation,不同的是,

  1. Mutation改变store中的状态,而Action提交的是Mutation
  2. Action 处理异步操作,Mutation必须是同步函数

Auction 接收一个和store实例具有相同方法的context对象,提交mutation的时候可以用context.commit()提交,获取state和getter时可以用context.statecontext.getters来获取。

组件中出发action用store.dispatch触发,

store.js
mutations: {
    updateTalk (state,payload) {
      state.talkMsg.push(payload);
    }
},
actions:{
    updateTalk({commit},msg){
        setTimeout(() => {
          commit('updateTalk',msg)
        }, 1000)
    },
},

borther.vue
this.$store.dispatch('updateTalk',parme);

module

module就是模块,当项目比较大,状态比较多的时候就需要状态按照模块划分开来,每一个模块都可以有自己的state,getter,mutation,action,也可以嵌套子模块。

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

辅助函数

vuex的辅助函数有:mapState,mapGetters,mapMutations,mapAuctions,辅助函数大同小异,以mapGetters为例,看一下Vuex里面,mapGetters是如何实现的。
先看mapGetters是如何使用的:使用前需要先引入,

import { mapGetters } from 'vuex'

mapGetters可以通过传入数组或者对象的方式使用,其中...并不是辅助函数提供的,而是es6中的展开运算符

  • 这种方式是传入数组的情况:
computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
  • 这种方式是传入对象的情况:
mapGetters({
  // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})

以上面传入对象的情况为例,解析之后会变成:

computed:{
    doneCount(){
        return this.$store.getters.doneTodosCount
    }
}

这是vuex里面mapGetters的实现代码:

var mapGetters = normalizeNamespace(function (namespace, getters) {
    var res = {};
    normalizeMap(getters).forEach(function (ref) {
      var key = ref.key;
      var val = ref.val;

      // The namespace has been mutated by normalizeNamespace
      val = namespace + val;
      res[key] = function mappedGetter () {
        if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
          return
        }
        if (!(val in this.$store.getters)) {
          console.error(("[vuex] unknown getter: " + val));
          return
        }
        return this.$store.getters[val]
      };
      // mark vuex getter for devtools
      res[key].vuex = true;
    });
    return res
  });

可以看到mapGetters主要用normalizeNamespacenormalizeMap方法实现的,我们先看一下normalizeMap是怎么实现的:

function normalizeMap (map) {
    return Array.isArray(map)
      ? map.map(function (key) { return ({ key: key, val: key }); })
      : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })
  }

可以看到normalizeMap方法主要是用来格式化getters参数的,这次以上面数组传入方式为例,通过getters方法之后的格式为:

[{
    key:'doneTodosCount',
    val:'doneTodosCount',
},{
    key:'anotherGetter',
    val:'anotherGetter',
}]

再看一下normalizeNamespace方法是怎么实现的:

function normalizeNamespace (fn) {
    return function (namespace, map) {
      if (typeof namespace !== 'string') {
        map = namespace;
        namespace = '';
      } else if (namespace.charAt(namespace.length - 1) !== '/') {
        namespace += '/';
      }
      return fn(namespace, map)
    }
  }

可以看出normalizeNamespace方法主要是处理命名空间的,就先不分析了,所以绝部分逻辑都是通过normalizeMap方法来处理的。

可以看出所谓的辅助函数,确实就是为了给使用者们带来便利的一种语法糖,大家不用把这个想的太复杂~

你可能感兴趣的:(Vuex入门)