在Store构造函数中html
// 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方法vue
咱们通常的commit使用方法第一个参数为字符串,第二个参数是咱们即将修改的数据vuex
store.commit('increment', { amount: 10 })
而官方文档还提供一种方式:对象风格的提交方式
提交 mutation 的另外一种方式是直接使用包含type
属性的对象:数组
store.commit({ type: 'increment', amount: 10 })
为何能这么调用呢ide
先粗略的看下原型对象的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方法,就是将这两种传参转换成同一种格式来使用ui
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的做用暂时还不了解,暂时没法分析。this
接下来看下const entry = this._mutations[type];
这里的_mutations将咱们在store对象中写的mutations以数组的格式,存在_mutations对象对应的值上。例如在spa
{ mutations: { addCount(state, payload) { state.count = state.count + 1; } } }
那么this._mutations('addCount')中就指向一个数组,数组中有addCount方法。prototype
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中找到待完善。。