Skip to content

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 就报错

总结

  1. 其不是按照树结构存储在 store 下的,而是按照命令空间扁平化的方式通过 getNamespace() 方法生成其唯一的字符串 key 存储在 store._mutations