在Store构造函数中

// bind commit and dispatch to self
const store = this;
const { dispatch, commit } = this;
this.dispatch = function boundDispatch (type, payload) {
  return dispatch.call(store, type, payload)
};
this.commit = function boundCommit (type, payload, options) {
  return commit.call(store, type, payload, options)
};

const { dispatch, commit } = this这里的commit指向的是Store.prototype.commit;
所以其实我们在实例中调用commit的时候传的参数经过一层中转,实际调用Store的原型对象的方法,
所以我们可以看到$store对象中有个commit方法,__proto__中也有一个commit方法,两个的差别仅仅是只传递参数的不同实际调用的是构造函数的原型对象的方法即$store.__proto__.commit方法
image.png

我们一般的commit使用方法第一个参数为字符串,第二个参数是我们即将修改的数据

store.commit('increment', {
  amount: 10
})

而官方文档还提供一种方式:对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含type属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

为什么能这么调用呢

先粗略的看下原型对象的commit方法

commit (_type, _payload, _options) {
    // check object-style commit
    const {
      type,
      payload,
      options
    } = unifyObjectStyle(_type, _payload, _options);

    const mutation = { type, payload };
    const entry = this._mutations[type];
    if (!entry) {
      {
        console.error(`[vuex] unknown mutation type: ${type}`);
      }
      return
    }
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload);
      });
    });
    this._subscribers.forEach(sub => sub(mutation, this.state));

    if (
      options && options.silent
    ) {
      console.warn(
        `[vuex] mutation type: ${type}. Silent option has been removed. ` +
        'Use the filter functionality in the vue-devtools'
      );
    }
  }

在调用commit方法的时候使用了unifyObjectStyle方法,就是将这两种传参转换成同一种格式来使用

function unifyObjectStyle (type, payload, options) {
  if (isObject(type) && type.type) {
    options = payload;
    payload = type;
    type = type.type;
  }

  {
    assert(typeof type === 'string', `expects string as the type, but found ${typeof type}.`);
  }

  return { type, payload, options }
}

最终返回的对象type是一个字符串,payload是荷载如果以对象风格的提交方式,经过转换后就是跟我们普通的提交荷载方式格式一样。
对于options的作用暂时还不了解,暂时无法分析。

接下来看下const entry = this._mutations[type];这里的_mutations将我们在store对象中写的mutations以数组的格式,存在_mutations对象对应的值上。例如在

{
    mutations: {
        addCount(state, payload) {
            state.count = state.count + 1;
        }
    }
}

那么this._mutations('addCount')中就指向一个数组,数组中有addCount方法。


this._withCommit(() => {
  entry.forEach(function commitIterator (handler) {
    handler(payload);
  });
});

所以调用的时候用forEach遍历数据,然后执行将载荷payload传入handler
那为什么要在_withCommit中调用方法呢,

_withCommit (fn) {
    const committing = this._committing;
    this._committing = true;
    fn();
    this._committing = committing;
  }

在提交时候讲this._commiting置为true,待执行完了fn,再将this._commiting = commiting
这里需要注意一个地方就是fn函数必须为同步函数,这样才能保证fn方法完全执行完了,才将this._committing置为false,这也是为什么官方文档中说到mutation-必须是同步函数因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的
那么这个_committing的作用是什么

function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`);
    }
  }, { deep: true, sync: true });
}

通过这段代码的提示,大概可以推断出其作用,当store的数据被修改,会触发上面的代码,如果通过commit方法改变数据,_committing此时是为ture的,而如果是直接修改store上面的数据,此时_committing还是false,通过_committing标识我们可以判断他是不是通过commit的方式进行修改的。
官方文档说过更改 Vuex的store中的状态的唯一方法是提交 mutation,这其实只是一个约定,只有这样数据的变化都可以在mutation中找到
待完善。。


crazyPupil
37 声望4 粉丝