官网说 Vuex 是为 Vue.js 应用开发的状态管理模式。个人理解就是管理全局变量的单例,支持响应式,存储状态或数据

可以用来处理多个页面数据共享,父子组件信息交互等麻烦的问题

本文基于用 Vue CLI 创建的脚手架,包括 State、Mutation、Action、Getter

引入 Vuex

在使用 Vue CLI 创建项目的时候引入 Vuex 就行

如果没有,需要安装 vuex 依赖,修改 main.js,还可以新建一个目录用于存放 Vuex 相关的代码,初学避免踩坑不建议这么整

脚手架中的相关文件

介绍一下脚手架中与 Vuex 相关的部分

src/store/index.js 是用于配置 Vuex 的全局单例,贴一下代码

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {}
});

src/main.js 这个文件里面 importsrc/store,并在 Vue 对象实例化时传入,没什么好说的,这里随便贴一个

import Vue from "vue";
import App from "./App.vue";
import store from "./store";

Vue.config.productionTip = false;

new Vue({
  store,
  render: h => h(App)
}).$mount("#app");

State 与 Mutation

简单说 State 就是全局变量,而且它发生变化时,其他与之关联的 DOM 也会更新

然后 Mutation 是用于修改 State 的方法,并且他能提供回滚等功能
官方不允许直接修改 State,虽然直接修改也能触发相应 DOM 的更新

然后我们尝试使用 State 在父子组件之间共享状态

首先在 src/store/index.js 中添加一个 showChr 状态以及用于修改这个状态的 Mutation,代码如下

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    showChr: false
  },
  mutations: {
    changeShowChr(state, v) {
      state.showChr = v
    }
  }
});

然后子组件 Chr.vue 的代码如下

<template>
  <div v-show="$store.state.showChr">
    <h1>这里是 Chr</h1>
    <button @click="$store.commit('changeShowChr', false)">隐藏</button>
  </div>
</template>

<script>
export default {};
</script>

最后父组件 Prt.vue 的代码如下

<template>
  <div>
      <button @click="$store.commit('changeShowChr', true)">显示</button>
      <chr></chr>
  </div>
</template>

<script>
import chr from "../components/Chr.vue";
export default {
  components: {
    chr: chr
  }
};
</script>

这样,子组件的显示与 showChr 关联,且父子组件都能修改它的值

注意:
Mutation 中不能包含异步操作
并且它也需要遵守 Vue 的响应规则,可以提前在 store 中初始化所有的属性或者使用 Vue.set 添加新的属性

另外再次强调:
虽然直接修改 State 也是响应式的,但官方明确说明必须使用 Mutation

Action

上面提到 Mutation 不能包含异步操作,如果需要异步,那么可以使用 Action

在 Action 中,可以使用 State 与 Getter 只读地访问状态,也可以提交 Mutation 修改状态,在其中能自由进行异步操作

调用 Action 时使用 $store.dispatch 方法。

于是我们在 src/store/index.js 中继续添加 Action,这个 Action 中做了异步操作后提交 Mutation,修改如下:

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    showChr: false
  },
  mutations: {
    changeShowChr(state, v) {
      state.showChr = v
    }
  },
  actions: {
    changeShowChr(context, v) {
      setTimeout(() => { context.commit('changeShowChr', v) }, 1000)
    }
  }
});

父组件不变,修改子组件如下:

<template>
  <div v-show="$store.state.showChr">
    <h1>这里是 Chr</h1>
    <button @click="$store.dispatch('changeShowChr', false)">隐藏</button>
    <p>{{$store.getters.listAPositiveCnt}}</p>
  </div>
</template>

<script>
export default {};
</script>

最后观察到点击隐藏,间隔一秒后子组件消失。

Module

把所有组件的状态或数据全部存储在 $store.state 中会很混乱且不好维护,可以把状态分割成多个模块(Module),每个模块都拥有自己的 State、Getter、Mutation、Action,也可以嵌套子模块
比较简单,下面是示例:

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const moduleA = {
  state: {
    msg: 'This is a'
  },
  mutations: {},
  actions: {},
  getters: {}
}

const moduleB = {
  state: {
    tmp: ' This is b'
  },
  mutations: {},
  actions: {}
}

export default new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

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

Getter

有时候,我们可以从 storestate 中派生出一些状态,例如对列表进行过滤然后计数。这种情况下我们可以在 store 中定义 Getter,下面贴一个简单的例子

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    listA: [-1, 2, -2, 1, 0, 5]
  },
  getters: {
    listAPositiveCnt: state => {
      return state.listA.filter(n => n > 0).length
    }
  }
});