超详细!Vuex手把手教程

1,前言 最近在重温vue全家桶,再看一遍感觉记忆更深刻,所以专门记录一下(本文vuex版本为v3.x)。
2,Vuex 是什么 Vuex是专为Vue.js开发的状态管理模式。它采用集中式存储,管理所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化(我的理解就是全局变量)。
3,5大属性说明 state
对象类型,类似于实例的 data属性,存放数据
getters
对象类型,类似于实例的计算属性 computed
mutations
对象类型,类似于实例的 methods,但是不能处理异步方法
actions
对象类型,类似于实例的 methods,可以处理异步方法
modules
对象类型,当state内容比较多时,通过该属性分割成小模块,每个模块都拥有自己的 state、mutation、action、getter
4,state 存储在state中的数据和Vue实例中的data遵循相同的规则,必须是纯粹的对象。
4.1 直接访问

this.$store.state.xxx

4.1 使用mapState映射

5,getters getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,并且默认接受state作为其第一个参数,也可以接受其他getter作为第二个参数(如下例)
5.1 先在vuex中定义getters
export default new Vuex.Store({ state: { count: 0, school: '清华大学' }, getters: { // 返回处理后的state值 getValue(state) { return state.count + '!' }, // 返回调用自身getters处理后的state值 getGetters(state, getters) { return state.school + getters.getValue }, // 接受外部传参后处理的值(在通过方法访问时,每次都会去进行调用,而不会缓存结果) getParam(state) { return (param) => { return state.school + param } } }, mutations: {}, actions: {}, modules: {} })

5.2 直接获取值
// 取值 console.log(this.$store.getters.getGetters) // 传参取值 console.log(this.$store.getters.getParam('param'))

5.3 使用mapGetters映射

6,Mutation 通过调用this.$store.commit('xxx'),调用mutation中的方法,更改store中的值
6.1,先在mutations中注册事件
export default new Vuex.Store({ state: { count: 0, school: '清华大学' }, getters: {}, mutations: { // 默认state作为第一个参数 handleAdd(state) { state.count++ }, // 接受传参 handleChange(state, value) { state.school = value } }, actions: {}, modules: {} })

6.2,在组件中调用方法commit修改值

6.3,使用常量定义方法名 新建文件mutation-types.js,定义方法名的常量,并导出
export const ADD_COUNT = 'ADD_COUNT' export const CHANGE = 'CHANGE'

在store中
import Vue from 'vue' import Vuex from 'vuex' import * as MT from './mutation-types'Vue.use(Vuex)export default new Vuex.Store({ state: { count: 0, school: '清华大学' }, getters: {}, mutations: { // 默认state作为第一个参数 [MT.ADD_COUNT](state) { state.count++ }, // 接受传参 [MT.CHANGE](state, value) { state.school = value } }, actions: {}, modules: {} })

在组件中

6.4,使用mapMutations映射

7,Action 注意,Action提交的是mutation,而不是直接变更状态,并且可以包含任意异步操作
7.1,在store中定义
import Vue from 'vue' import Vuex from 'vuex' import * as MT from './mutation-types'Vue.use(Vuex)export default new Vuex.Store({ state: { count: 0, school: '清华大学' }, getters: {}, mutations: { // 默认state作为第一个参数 [MT.ADD_COUNT](state) { state.count++ }, // 接受传参 [MT.CHANGE](state, value) { state.school = value } }, actions: { add(context) { context.commit(MT.ADD_COUNT) } }, modules: {} })

7.2,在组件中使用

7.3,使用mapActions映射
import { mapActions } from 'vuex'methods: { ...mapActions([ 'moduleFn' ]) }

或者
import { mapActions } from 'vuex'methods: { ...mapActions([ fn: 'moduleFn' ]) }

7.4,简化写法 Action接受一个与store实例具有相同方法和属性的context参数对象,因此你可以调用context.commit提交一个mutation,或者通过context.statecontext.getters来获取stategetters,利用ES6的解构,可以简化写法。
actions: { add({ commit, state }) { commit(MT.CHANGE, state.school) } }

7.5,执行异步操作 在vuex中
import Vue from 'vue' import Vuex from 'vuex' import * as MT from './mutation-types'Vue.use(Vuex)export default new Vuex.Store({ state: { count: 0 }, getters: {}, mutations: { // 默认state作为第一个参数 [MT.ADD_COUNT](state) { state.count++ } }, actions: { add({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit(MT.ADD_COUNT) resolve() }, 1000) }) } }, modules: {} })

在组件中使用async / await或者then / catch处理异步

8,Modules 当应用变得非常复杂时,store对象就可能变得相当臃肿。这时候可以将store分割成模块,每个模块拥有自己的statemutationactiongetter、甚至是嵌套子模块,从上至下进行同样方式的分割。
8.1,准备工作 在store目录下新建Modules文件夹,在Modules文件夹中新建modulesA.jsmodulesB.js,如下图
超详细!Vuex手把手教程
文章图片

在modulesA.js中写上局部模块的statemutationactiongetter,并导出
const moduleA = { state: () => ({ a: '我是moduleA' }), getters: {}, mutations: {}, actions: {} }export default moduleA

然后在storeindex.js中引入,并丢进modules对象里
import Vue from 'vue' import Vuex from 'vuex' import * as MT from './mutation-types' import moduleA from './modules/moduleA' import moduleB from './modules/moduleB'Vue.use(Vuex)export default new Vuex.Store({ state: { count: 0 }, getters: {}, mutations: {}, actions: {}, modules: { moduleA, moduleB } })

8.2,使用modules中注入的模块的state 在组件中直接使用
this.$store.state.moduleA.xxx

在组件中使用mapState映射
{{ moduleA.xxx }}import { mapState } from 'vuex'computed: { ...mapState([ 'moduleA' ]) }

8.3,使用modules中注入模块的getters 在组件中直接使用
this.$store.getters.getModuleA

在组件中使用mapState映射
{{ getModuleA }}
import { mapGetters } from 'vuex'computed: { ...mapGetters([ 'getModuleA' ]) }

模块内部的getter,接受的参数stategetters是模块的局部状态对象,而根节点的状态会作为第三个参数rootState暴露出来
const moduleA = { getters: { getModuleA(state, getters, rootState) { return state.xxx + '---' + rootState.xxx } } }

如果需要带参数
const moduleA = { getters: { getModuleA(state, getters, rootState) { return (value) => { return state.a + '---' + value } } } }

8.4,使用modules中注入模块的mutations 在组件中直接使用
this.$store.commit('setModuleA') || this.$store.commit('setModuleA', '参数')

在组件中使用mapMutations映射
import { mapMutations } from 'vuex'methods: { ...mapMutations([ openFn: 'setModuleA' ]) }

模块内部的mutations,默认接受的第一个参数state是模块的局部状态对象
const moduleA = { mutations: { setModuleA(state) { state.xxx += 'xxx' } } }

如果需要带参数
const moduleA = { mutations: { setModuleA(state, value) { state.xxx += value } } }

8.5,使用modules中注入模块的actions 在组件中直接使用
this.$store.dispatch('xxx')

在组件中使用mapActions映射
import { mapActions } from 'vuex'methods: { ...mapActions([ 'moduleA' ]) }

或者重命名
import { mapActions } from 'vuex'methods: { ...mapActions({ fn: 'moduleA' }) }

对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState
const moduleA = { // ... actions: { fn ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } }

8.6,命名空间 默认情况下,模块内部的actionmutationgetter是注册在全局命名空间的,这样使得多个模块能够对同一mutationaction作出响应。如果希望模块具有更高的封装度和复用性,可以通过给模块添加namespaced: true的方式使其成为带命名空间的模块。当模块被注册后,它的所有getteractionmutation都会自动根据模块注册的路径调整命名。
8.6.1,使用
先在模块moduleB.js中添加namespaced: true
const moduleB = { namespaced: true, state: () => ({ b: '我是moduleB' }), mutations: {}, actions: {}, getters: {} }export default moduleB

storeindex.js
import moduleA from './modules/moduleA' import moduleB from './modules/moduleB'export default new Vuex.Store({ state: {}, getters: {}, mutations: {}, actions: {}, modules: { moduleA, moduleB } })

如果在组件中使用命名空间,需要带上空间名称,mapState, mapGetters, mapMutationsmapActions用法一样。

8.6.2 ,在带命名空间的模块中内访问全局内容
如果你希望使用全局的stategetterrootStaterootGetters会作为第三和第四参数传入getter,也会通过context对象的属性传入action。若需要在全局命名空间内分发action或提交mutation,将{ root: true }作为第三参数传给dispatchcommit即可
const moduleA = { namespaced: true, state: () => ({ a: '我是moduleA' }), getters: { getModuleA(state, getters, rootState, rootGetters) { // 使用全局命名空间的state或getters return state.a + rootState.count } }, mutations: { setModuleA(state) { console.log(state.a) } }, actions: { addM({ state, commit, dispatch, rootState, rootGetters }) { console.log(rootState) console.log(rootGetters) // 调用全局命名空间的方法 dispatch('rootFunction', null, { root: true }) } } }export default moduleA

8.6.3,在带命名空间的模块注册全局action
在带命名空间的模块注册全局action,需要添加root: true,并将这个action的定义放在函数handler中,其中,handler的第一个参数namespacedContext就是action中的Context参数
const moduleA = { namespaced: true, state: () => ({ a: '我是moduleA' }), getters: {}, mutations: {}, actions: { rootFn: { root: true, handler(namespacedContext, param) { console.log(namespacedContext.state) } } } }export default moduleA

如果看了觉得有帮助的,我是@鹏多多,欢迎 点赞 关注 评论;END
往期文章
  • 使用nvm管理node.js版本以及更换npm淘宝镜像源
【超详细!Vuex手把手教程】个人主页
  • CSDN
  • GitHub
  • 简书
  • 博客园
  • 掘金

    推荐阅读