【Vue菜鸟日记】Vuex

前端菜鸟学习Vue - Vuex

前言

【Vue菜鸟日记】系列文章是一个前端菜鸟对 Vue 的日常学习与总结,属个人理解,过程及思路优先。如果存在错误,请仔细甄别与赐教。

什么时候使用Vuex

当多个组件需要读取/修改同一个数据时,如果不使用 Vuex,使用事件机制(on/emit)传递数据/传递数据变化会非常繁琐。而数据放在 Vuex 中,多个组件与 Vuex 进行交互可以简化。常见场景:购物车、用户登录信息等

Vuex架构

image-20250324091534567

Vuex = actions + mutations + state + store

由 store 管理actions、mutations、state,并提供 API:dispatch、commit

基本使用流程

写入(标准做法)

  • vue 文件中调用 dispatch 触发 Actions 中对应的方法
  • actions 的方法中,再调用 commit 触发 mutations 中对应的方法
  • mutations 的方法中,再修改 state 的数据

写入(特殊做法)

如果 actions 没有额外的逻辑,只是再直接调用commit 转给mutations 时, actions 似乎很没用,这种情况下,Vuex 也允许直接调用 mutations 的方法

  • vue 文件中调用 commit 触发mutations 中对应的方法
  • mutations 的方法中,再修改 state 的数据

读取

  • vue 文件中,使用$store.state.xxx 读取数据

基本使用方法

  • 使用 npm安装,Vue2 对应 Vuex3,Vue3 对应 Vuex4

  • 配置一个 Vuex.store 对象

    • import Vue from 'vue'
      import Vuex from 'vuex'
      
      Vue.use(Vuex)
      
      const actions = {
      	add(context, value){
      		console.log("add", context, value)
      		context.commit('ADD', value)
      	}
      }
      const mutations = {
      	ADD(state, value){
      		console.log("ADD", state, value)
      		state.sum += value
      	}
      }
      const state = {
      	sum:0
      }
      
      const store = new Vuex.Store({
      	actions,
      	mutations,
      	state
      })
      export default store
      
  • 将 store 对象传入 new Vue 的参数里,这时 Vue 对象和 Vue 组件对象上都会有$store(插件机制)

  • 写入

    • this.$store.dispatch('方法', 参数) //标准做法
      
    • this.$store.commit('方法', 参数) //特殊做法
      
  • 读取

    • {{$store.state.sum}}
      

简化插值

再进行读取时,每次都得写$store.state.xxx显得很麻烦, 可以使用计算属性的方式,转换为计算属性的名

但是即使是这样,计算属性的方法中,还是得一直写 return this.$store.state.xxxreturn this.$store.state.yyy

由于代码有很高的相似度,能否写一个函数来自动生成这些方法?

Vuex 提供了这样的机制。mapStatemapGetters

import {mapState} from 'vuex'

computed{
	//假设手写计算属性时是这样
	//he(){
	//  return this.$store.state.sum
	//}
	
	//那么使用下面的方式就不用自己写上面的计算属性了
  ...mapState({he: 'sum'}) //这个会得到一个多个函数的对象,所以使用对象展开运算符...
  
  //或者可以使用数组写法,计算属性名与 state 中的名是一样的
  ...mapState(['sum', 'xxx', 'yyy'])
}

getter

Vuex 中的 getter 就像是 Vue 中的计算属性

通过实例化 Vuex.Store 时传入 getters对象,可以在插值时使用 store.getters.xxx

与 mapState 一样,mapGetters 也是一样的用法

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

...mapGetters({
  // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})

Actions和Mutations也有辅助函数

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}
import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

模块化

可以按照不同功能对actions、mutations、state 中的代码进行分类整理,从而可以更进一步把代码拆到独立文件中,这样会带来下面的改变

实例化Store时

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

变更为

const aOptions = {
	actions: {xxx},
	mutations: {xxx},
	...省略
}//可以放在独立文件中

const store = new Vuex.Store({
	modules: {
		a: aOptions,
		...省略
	}
})

命名空间

在模块对象中添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

读取state时

原本读取 store.state.xxx 会变为 store.state.a.xxx

如果使用辅助函数,需要添加模块名

...mapState(['a']) //要么直接映射 a,然后再 a.xxx
...mapState('模块', ['xxx']) //如果不想写 a.xxx而是想用 xxx,那么这么写

读取getter时

如果使用了模块,getter 不像 state 那样,而是直接变更了函数名,就像这样

原本的方法 getX 变更为 a/getX

所以基本写法要写成 this.$store.getters['a/getX']

如果使用辅助函数 ...mapGetters('a', ['x'])

调用dispatch时

基本写法 this.$store.dispatch('a/XXX', value)

如果使用辅助函数 ...mapActions('a', {....})

调用 commit 时

基本写法 this.$store.commit('a/XXX', value)

如果使用辅助函数 ...mapMutations('a', {....})