Skip to content

门泊吴船亦已谋

Vuex 完全学习笔记

官网说,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
它 import 了 src/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

有时候,我们可以从 store 的 state 中派生出一些状态,例如对列表进行过滤然后计数。这种情况下我们可以在 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
    }
  }
});