前言
【Vue菜鸟日记】系列文章是一个前端菜鸟对 Vue 的日常学习与总结,属个人理解,过程及思路优先。如果存在错误,请仔细甄别与赐教。
什么时候使用Vuex
当多个组件需要读取/修改同一个数据时,如果不使用 Vuex,使用事件机制(on/emit)传递数据/传递数据变化会非常繁琐。而数据放在 Vuex 中,多个组件与 Vuex 进行交互可以简化。常见场景:购物车、用户登录信息等
Vuex架构
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.xxx
,return this.$store.state.yyy
由于代码有很高的相似度,能否写一个函数来自动生成这些方法?
Vuex 提供了这样的机制。mapState
、mapGetters
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', {....})