写在前面

Vuex 是一个专门为 vue.js 应用程序开发的状态管理模式。

它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

以上是 Vuex 官网 对 Vuex 的描述,乍一看会有些懵。在上篇博客 Vue 简单状态管理—store 模式 中介绍过 简单 store 模式的状态管理。可以对应 store 对象中的 state 属性存储的就是组件的共享数据状态,因此,state 属性的存在就是上述定义中的 集中存储管理应用的所有组件状态

在 store 模式中,组件修改 store 中的数据状态必须使用 store 提供的方法修改,不允许直接修改 store.state 值,在 Vuex 中也是一样的。store 模式中各组件在修改 state 状态值时,并没有记录每次具体做了什么样的修改,因此无法记录每次的状态改变。

但在 Vuex 中记录了每次状态修改的详细信息,可以方便地跟踪每一个状态的变化,实现方式就是 commit mutations(mutations 就是变化的意思,commit mutations 是提交变化的意思,Vuex 规定修改状态要提交修改申请,然后让 store 实例调用申请的修改方法,而不是组件自己直接调用 store 中的修改方法)。因此这种不允许组件直接修改 store 中的状态,而应执行 commit mutation 通知 store 去改变状态的约定,就是上述定义中的 以相应的规则保证状态以一种可预测的方式发生变化

1. 自定义 store 模式变为 Vuex 模式

无论是 Vuex 模式还是自定义 store 模式的核心都是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。

为了方便地记录每次的状态改变,Vuex 模式将我们自定义 store 简单状态管理模式中的修改 store.state 状态的方法都统一放在了 mutations 对象属性里。组件想要改变状态时就调用 Vuex 的 store 实例提供的 store.commit() 接口进行变化的提交。将上篇博客的自定义简单 store 模式修改为 Vuex 模式如下:

  1. //不再需要原来的 store.js 了,而是直接在 main.js 中定义 Vuex 的 store 实例
  2. import Vue from 'vue'
  3. import App from './App.vue'
  4. import Vuex from 'vuex'
  5. Vue.config.productionTip = false
  6. Vue.use(Vuex)
  7. const store = new Vuex.Store({
  8. state: {
  9. todos: [
  10. {text: '写语文作业', done: false},
  11. {text: '做数学卷子', done: false}
  12. ],
  13. x: 'hi'
  14. },
  15. mutations: {
  16. addTodo(state, str){
  17. const obj = {text: str, done: false}
  18. state.todos.push(obj)
  19. },
  20. setDone(state, index){
  21. state.todos[index].done = true
  22. },
  23. setHi(state, str){
  24. state.x = str
  25. }
  26. }
  27. })
  28. new Vue({
  29. render: h => h(App),
  30. store //注入 store 机制,这样从根元素到所有子组件都可从 this.$store 访问到 store 实例
  31. }).$mount('#app')
  1. //A.vue
  2. <template>
  3. <div class="A">
  4. 我是 A组件 {{x}}
  5. <ul>
  6. <li v-for="(todo,index) in todos"
  7. :key="index" :class="todo.done?'done':''" @click="setDone(index)">
  8. {{todo.text}}
  9. </li>
  10. </ul>
  11. </div>
  12. </template>
  13. <script>
  14. export default {
  15. name: 'A',
  16. computed: {
  17. todos(){
  18. return this.$store.state.todos
  19. },
  20. x(){
  21. return this.$store.state.x
  22. }
  23. },
  24. methods: {
  25. setDone(index){
  26. this.$store.commit('setDone',index)
  27. }
  28. }
  29. }
  30. </script>
  31. <style scoped>
  32. .A{
  33. background: red;
  34. color: white;
  35. padding: 20px;
  36. }
  37. .A li.done{
  38. background: green;
  39. }
  40. </style>
  1. //B.vue
  2. <template>
  3. <div class="B">
  4. <div>
  5. 我是 B 组件,在下方输入框输入任务在 A组件 中添加任务
  6. </div>
  7. <input type="text" v-model="text">
  8. <button @click="addTodo">add todo</button>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. name: 'B',
  14. data(){
  15. return {
  16. text: ''
  17. }
  18. },
  19. methods:{
  20. addTodo(){
  21. if(this.text){
  22. this.$store.commit('addTodo',this.text)
  23. this.$store.commit('setHi',this.text)
  24. }
  25. }
  26. }
  27. }
  28. </script>
  29. <style scoped>
  30. .B{
  31. background: yellow;
  32. padding: 20px;
  33. }
  34. </style>

2.state 与 mutations

state 和 mutations 是创建一个 Vuex 的 store 实例的最基本的构成。

2.1 state

Vuex 使用单一状态树,每个应用仅仅只包含一个 store 实例,因此,state 用于存放整个应用的全部状态。

那么每个组件如何读取 store 实例中的状态呢?

最简单的就是在计算属性中返回某个状态,一定得是在计算属性 computed 中获取,不能在 data 中获取,因为在计算属性中返回时,当计算属性依赖的状态变化时,属性会重新计算,视图随之更新。若在 data 中获取,只会执行一次,不会根据状态的改变而改变,无法进行数据响应式。

  1. computed: {
  2. todos(){
  3. return this.$store.state.todos
  4. },
  5. x(){
  6. return this.$store.state.x
  7. }
  8. }

当一个组件需要获取多个状态的时候。可使用 mapState 获取

  1. import { mapState } from 'vuex' //引入 mapState
  2. //当原模原样返回状态时
  3. computed: mapState([
  4. 'todos', // 映射 this.todos 为 store.state.todos
  5. 'x'
  6. ])
  7. //当为状态重命名时
  8. computed: mapState({
  9. todos: state => state.todos,
  10. x: 'x'
  11. })
  12. //为了能够使用 `this` 获取局部状态,必须使用常规函数
  13. compted: mapState({
  14. countPlusLocalState (state) {
  15. return state.count + this.localCount
  16. }
  17. })
  18. //当状态计算属性与局部计算属性混合使用时
  19. computed: {
  20. localComputed () { /* ... */ },
  21. // 使用对象展开运算符将此对象混入到外部对象中
  22. ...mapState({
  23. // ...
  24. })
  25. }

2.2 mutations

mutation 必须是同步函数

mutations 对象属性里存放的是改变状态的方法。这些方法都会接受 state 作为第一个参数。

这些方法不能由组件直接调用,组件需要通知 store 实例调用,通知方法只有一种就是提交申请:store.commit()

(1) 无载荷提交
在 mutations 中,载荷就是参数的意思,无载荷就是在提交时无需传递参数的意思,如下:

  1. mutations: {
  2. increment (state) {
  3. // 变更状态
  4. state.count++
  5. }
  6. }
  7. store.commit('increment')

(2) 有载荷提交
当提交需要给调用的方法传递参数时,这些方法会接受 payload 作为第二个参数。如下:

  1. mutations: {
  2. increment (state, n) {
  3. state.count += n
  4. }
  5. }
  6. store.commit('increment', 10)
  7. //在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
  8. mutations: {
  9. increment (state, payload) {
  10. state.count += payload.amount
  11. }
  12. }
  13. store.commit('increment', {
  14. amount: 10
  15. })

store.commit() 有两种提交方式。一种是提交两个参数,第一个是回调函数的名字,第二个是传递的参数。另外一种是直接提交一个信息对象,包含所有的提交信息,如下:

  1. store.commit({
  2. type: 'increment',
  3. amount: 10
  4. })
  5. //当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数
  6. mutations: {
  7. increment (state, payload) {
  8. state.count += payload.amount
  9. }
  10. }

3. getters 与 actions

介绍完了 store 实例构造选项中最基本的 state 和 mutations 选项后,就是对上述选项的补充扩展选项的介绍。

3.1 getters

state 选项中的数据都是基本的元数据,但是可能会有一些由基本数据计算出来的数据需要经常用到,因此 Vuex.Store 提供了计算属性选项,就像 Vue组件 中的 computed 计算属性一样。使用 getters 构造选项存储 store 的计算属性,同样地,计算属性的计算函数将 state 作为第一个参数。

3.2 actions

mutations 选项中的方法都必须是同步函数,不支持异步函数。为了支持改变状态的异步函数,Vuex.Store 提供了 actions 构造选项。

Action 通过 store.dispatch 方法触发

  1. store.dispatch('increment')

总结

Vuex 更深入的 getters 和 actions 的用法就不再举例了,举例也是根据官网抄的。当用到时可去官网查看。