本篇文章仅适合基础回顾,不具备深度

一、vuex 的基本描述

vue 的中文文档支持的足够好,不需要重复文档

文档地址:https://vuex.vuejs.org/zh/

核心思想(流程):

https://vuex.vuejs.org/vuex.png

如果熟悉 Flux 架构,则对 vuex 的这套基本的流程体系也基本就能够理解,这里不不再重复。

相比于 Flux,vuex 对其进行了 vue 的适配,借助 vue 的 细粒度响应机制(文档说的)进行高效的状态更新,基本上大型项目如果要进行状态管理,直接就是 vuex 就可以,而 React 系,则存在 Flux、Redux、MobX 等等。

有些时候官方规范化之后也有好处。

安装引入很简单:

yarn add vuex

import Vuex from 'vuex'

二、Store 及基本使用

严格来说,Store 的概念并不是 vuex 的直接子集属性,每个 vuex 的核心就是 Store,也可以称之为仓库,而这个 Store 就是一个单一状态管理容器。

Store 中可以配置 State、getter、mutation 等属性,而Store 的属性涵盖了对一个数据的初始化、获取及更新操作。

Store 维护了一套响应式的状态存储机制,也就是当 Store 的数据发生变化是,会通知 Vue 组件,进行响应式的更新。

一个 Store 会有一些约定好的属性,因此定义一个 Store 的过程基本如下:

引入 Vue 及 Vuex,并启用 Vuex:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

一定要确保 Vue.use(Vuex);new Vuex.Store({}) 之前调用, 否则你会收到这样一个错误:

[vuex] must call Vue.use(Vuex) before creating a store instance.

定义 Store 的 state:

const state = {
  count: 0
}

定义 Store 的 getters:(如果需要)

const getters = {
  getCount(state) {
    return state.count;
  }
}

这样可以不通过 this.$store.state.count 的形式访问,而是可以通过 this.$store.getters.getCount 的形式访问(当然还有更简洁方式),出了访问方式变动之外,可以在函数中进行一系列操作,然后在返回数据。

定义 Store 的 mutations:

const mutations = {
  increment(state){
    state.count ++ ;
  }
}

getters 是获取数据,而 mutations 则是显示的对数据进行更改。 Store 的 state 是不允许通过 this.$store.state.count = 2 的形式去修改的,必须显示的提交修改才能去执行修改。

提交修改的方式是 this.$store.commit(mutationName)

将 state、getters 以及 mutations 作为初始化内容传入 new Vuex.Store({}):

const store = new Vuex.Store({
 state,
 getters,
 mutations
});

export default store;

最后将 store 导出,然后在 new Vue({}) 的地方引入,挂载到 store 属性下即可。

import store from './store';

new Vue({
  el: '#app',
  router,
  store,
  render: (c) => {return c(App)}
});

三、state

Vuex 是一个单一的状态树来管理状态,而 Store 则是Vuex 的存储仓库,每个 Store 中真实表示数据或者状态的则是 Store 的 state 属性。

如果熟悉 React 则就无需强调什么是 state 了, 所有的数据初始化定义都应该在 state 中定义,并且获取数据也可以从 state 中取,只是无法直接去更改某个 state 的值。

比如在模板中读取某个 state 的值的方式:

<p>count is: {{this.$store.state.count}}</p>
console.log(this.$store.state.count)

而在如果你没有使用 getters 的话,一般也会推荐将 state 的值初始化给组件computed 的某个属性,能够简化很多 this.$store.state.xxx 的代码。

比如我将state.count 赋值给 computed 中的 count

    computed: {
      count() {
        return this.$store.state.count
      }
    },

这样在模板中我可以直接使用 count:

<p>count is: {{count}}</p>

mapState

我本人不觉得 mapState 有什么特别大的帮助,只是在开发上可能少写点代码而已

使用 mapState 需要首先从 vuex 中引入

  import {mapState} from 'vuex';

即使每次初始化 computed,也需要写大量的 this.$store.state.xx 因此 vuex 暴露一个 mapState 的属性,用来边界开发,尤其是将 state 的值初始化给 computed 的时候:

  computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,

    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

上面 computed 的定义中,直接是运行了 mapState 方法, 然后参数是一个对象:

  • count: state => state.count:定义一个 computed:count,通过一个函数传入 state 参数,并返回 state.count
  • countAlias: 'count', 直接对 state.count 进行重命名
  • countPlusLocalState (state) { return state.count + this.loca.... } 如果需要获取 本组件的一些状态,则必须使用常规函数的形式,并且将 state 传入函数,其中可以通过 this.xxx 获取局部状态。

如果想直接用 state 的名称,可以直接给 mapState 传一个数组:

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

而如果本地也有 computed 数据,然后也需要 state 初始化一些本地数据,借助 mapState 实现的话,需要进行对象展开符,当然,mapState 该怎么用还是怎么用,与上面一样。

computed: {
  localComputed () { /* ... */ },
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    // ...
  })
}

四、Getters

getters 只是为了更加方便的获取数据,比如一个 count state,如果不同的组件需要不同结果的 count,则一般都是由业务组件去做这个事情,而 store 也可以把这个事情给做掉。

举例:我需要在每个 state.count 的值前面,在加上一个 state.num,组合成一个新的值给多个组件使用。

如果组件完成这个事情,则需要有组件去实现,(包括金钱、时间的格式化,内容过滤等操作),而通过 getter 则可以提供统一的访问形式。

1、getter 的定义及基本使用

getter 的定义也很简单,定义某个属性,方法中形参是 state:

  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }

通过传入的 state 参数,可以访问当前 store 的状态,进行一系列的判断过滤等。

2、多个 getter 配合

getter 也可以结合其他的 getter 一起使用,getter 定义的第二个参数可以是其他 getter:

getters: {
  doneTodos: state => {
      return state.todos.filter(todo => todo.done)
  },
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

上面的例子中,首先我已经定义完了 doneTodos 这个 getter,但是比如我还需要获取完成的 todo 的数量,如果我还是使用 state 的话,我还需要写一遍遍历判断最后计数。

而我直接使用当前 getters.doneTodos.length 就不需要再去写那些代码了。

3、getter 传参

除此之外,还可以通过方法访问 getters,所谓的方法访问无非就是传递参数,来进行不同的预期处理:

getters: {
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

比如上面的 getters 支持通过 id 查找 todo,并且返回。

当定义好之后,就能够直接通过下面的方式查找需要的 todo:

this.$store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

而这种定义方式,无非就是一个科里化,没有什么新奇的玩意儿。

4、mapGetter

mapGetter 的使用需要首先引入 import { mapGetters } from 'vuex'

mapGetter 如同 mapState 一样,没什么特别新奇的功能,也是一种辅助作用,写代码比较轻松,必须要写那么多的 this.$store.getters.name

而使用的形式也非常类似于 mapState

  computed: mapGetter({
    // 箭头函数可使代码更简练
    count: getters=> getters.getCount,

    // 传字符串参数 'count' 等同于 `getters=> getters.getCount`
    countAlias: 'getCount',

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (getters) {
      return getters.getCount+ this.localCount
    }
  })
}