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方法来处理的。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270