4.3.4.3 Mutation
提交载荷(Payload)
听起来好像很复杂,举个栗子就知道了:
我们在mutations中定义一个函数,需要从组件传参来执行,参数即是载荷
const store = createStore({
state: {
count: 1
},
mutations: {
increment(state,n){ //这个n呢就是需要从组件传过来的参数了
state.count += n
}
}
})
组件中使用increment:
store.commit('increment',10)
而如果我们在后续,需要往这一个函数里面继续加参数,那这个函数写起来岂不是很长,像这样increment(state,n,m,o,p,q,r,s,t)非常怪异。所以载荷一般会写成对象:
const store = createStore({
state: {
count: 1,
name: ''
},
mutations: {
increment(state,payload){ //这个n呢就是需要从组件传过来的参数了
state.count += payload.a
name = payload.b
......
}
}
})
在组件中调用:
store.commit('increment',{
a:1,
b:'芜湖'
})
对象风格的提交方式
提交mutation的另一种方式是直接使用包含type属性的对象,还拿上面那个mutation为例子:
store.commit({
type:'increment',
a:1,
b:'芜湖'
})
使用常量代替 Mutation 事件类型
把mutaion时间统一到一个js文件中管理,在多人协作的大项目中会比较有帮助:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = createStore({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能
// 来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// 修改 state
......
}
}
})
在组件中使用:
import { SOME_MUTATION } from './mutation-types' //同样要引入
//使用
store.commit(SOME_MUTATION)
在mutation中必须写同步函数,mutation中的异步函数会让devtools捕捉不到它的改变。但也有相对应专门来写异步函数的地方 action。
那么跟State和Getter一样,他们有mapState和mapGetters,mutation也有mapMutations:
methods:{
//第一种写法 数组,这样写 使用的时候就是 this.increment()
...mapMutations(['increment'])
//第二种写法 载荷,这样写 使用的时候就是 this.add()
...mapMutations({
add:'increment'
})
}
4.3.4.4 Action
Action 类似于 mutation, 不同点在于:
- Action 提交的是 mutation,而不是直接变更状态
- Action 可以包含任意异步操作
写个小栗子:
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
实践中,我们会经常用到ES2015的解构来简化代码:
actions: {
increment ({context}) {
commit('increment')
}
}
分发Action,我们回忆mutation的调用是state.commit('increment'),action的触发方式是store.dispatch('increment')。到目前位置都和mutation没有本质上的区别,但上面我们说过了。Action内部可以执行异步操作:
actions:{
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
同样的,Action也有mapActions:
import { mapActions } from 'vuex'
export default {
methods: {
//第一种写法 使用时是:this.increment()
...mapActions(['increment']),
//第一种写法 使用时是:this.add()
...mapActions({
add: 'increment'
})
}
}
4.3.4.5 Module
当我们的项目逐渐壮大,在state中需要管理的数据就会越来越多,如果全部放在一起的话,到时候就非常地乱不好管理,store对象就有可能变得相当臃肿。
为了解决以上问题,Vuex允许我们将store分割成模块(module) 。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块----从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
这里可以先了解一下Vuex进阶的项目结构:
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── moduleOne.js # 模块一
└── moduleTwo.js # 模块二
像结构中,我们在store文件夹中再创建一个modules文件夹,该文件夹下便可以放store的各个模块。store文件夹下分别有actions.js,mutations.js。分别接收模块中的action、mutation。而里面的index.js正是将所有模板聚集的地方,举个栗子:
//index.js
import {createStore} from 'vuex'
import moduleOne from '@/store/modules/moduleOne'
import moduleTwo from '@/store/modules/moduleTwo'
import actions from '@/store/actions'
import mutations from '@/store/mutations'
const store = createStore({
modules:{
a:moduleOne,
b:moduleTwo
},
actions, //moduleOne和moduleTwo的acttion都放到这个actions里
mutations //moduleOne和moduleTwo的mutation都放到这个mutations里
})
export default store
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
//moduleOne.js
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}