Appearance
Mutation
使用方式
js
moduleABA.mutations: {
// ...
increment (state) {
// 变更状态
state.count++
}
}
store.commit("moduleA/modueleAB/modueleABA/increment") // 1 -> 2
初始化阶段
遍历处理 module 中的 mutations 属性,将每一个 action 按照以其 namespaced + / + actionNameKey
为 key 存储到 store._mutations[type]
下。这样上面moduleA/modueleAB/modueleABA/increment
就会变成 store._mutations["moduleA/modueleAB/modueleABA/increment"] = () => {}
这边根 action 一样两组不同的对象 local.state
store.state
js
/**
*
* @param {*} store store实例对象
* @param {*} type namespacedType mutation的全路径名称 如: ‘a/aa/aaa/mutation1’
* @param {*} handler 子module 模块实例对象
* @param {*} local { dispatch , commit, getters , state } 的数据对象
*/
function registerMutation(store, type, handler, local) {
//以mutation的全路径名称为key 保存在 store._mutations上
const entry = store._mutations[type] || (store._mutations[type] = [])
entry.push(function wrappedMutationHandler(payload) {
handler.call(store, local.state, payload)
})
}
mutations 的访问
访问方式为 store._mutations["moduleA/modueleAB/modueleABA/increment"] = () => {}
核心源码为:
js
/**
* 提交 mutation。options 里可以有 root: true,它允许在命名空间模块里提交根的 mutation
*
* @param {*} _type // commit的路径
* @param {*} _payload // commit的值
* @param {*} _options //commit的配置options 现在只支持 {root:true}
* @memberof Store
*/
function commit(_type, _payload, _options) {
// check object-style commit
// 处理入参 提供两种方式 传递 type, payload, options
const { type, payload, options } = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
// 根据 type 即 mutation的全路径 获取 处理函数
const entry = this._mutations[type]
if (!entry) {
if (process.env.NODE_ENV !== "production") {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
// TODO: 为什么不直接回调 entry 而通过_withCommit去回调
this._withCommit(() => {
// 回调处理 调用的 mutation
entry.forEach(function commitIterator(handler) {
/*
为什么此时传递的只有 payload,而我们mutation的入参为 两个 ({state,commit,getters},payload)
因为这时候调用的是 store._mutations('a/aa/mutation1',function)中的方法,而不是直接调用module.mutation我们定义的mutation
而在registerMutation()中
entry.push(function wrappedMutationHandler(payload) { //这个payload 才是这是的入参 payload
handler.call(store, local.state, payload)
})
*/
handler(payload)
})
})
this._subscribers.forEach(sub => sub(mutation, this.state))
if (process.env.NODE_ENV !== "production" && options && options.silent) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
"Use the filter functionality in the vue-devtools"
)
}
}
这边又一个比较重要的地方:
为什么不直接回调 entry 而通过_withCommit(() => {})
去回调
答案:
这边就是通过全局唯一的一个状态属性 store._committing
去维护数据修改状态的变化,从而防止我们在 commit 执行的过程中调用执行其他的 commit 或者 action。
如:
js
moduleABA.mutations: {
// ...
increment : (state) => {
store.commit("decrement"); // 调用其他的 commit
// 变更状态
state.count++
}
}
详细答案:
我们每次在修改 state 的时候 如 commit
replaceState
等,不是直接调用 store._mutations
的方法 而是通过 this._withCommit( function(){ hander() })
去处理。 因为每次执行状态 state 的修改的时候 保证 this._committing
为 true,那么在追踪状态变化的时候,如果这个不为 true,那么说明这次修改不是正确的。 而在 enableStrictMode()
即 store.strict = true
的时候 store._vm.$watch(this._data.$$state)
如果 store._committing
不为 true 就报错
总结
- 其不是按照树结构存储在 store 下的,而是按照命令空间扁平化的方式通过 getNamespace() 方法生成其唯一的字符串 key 存储在
store._mutations
下