Vuex 源码解析(三)commit 、 dispatch 实现 、辅助函数实现 、registerModule
commit
/src/store.js
commit (_type, _payload, _options) {
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
const entry = this._mutations[type]
if (!entry) {
if (__DEV__) {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
this._subscribers
.slice()
.forEach(sub => sub(mutation, this.state))
if (
__DEV__ &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
'Use the filter functionality in the vue-devtools'
)
}
}
dispatch
/src/store.js
dispatch (_type, _payload) {
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
if (!entry) {
if (__DEV__) {
console.error(`[vuex] unknown action type: ${type}`)
}
return
}
try {
this._actionSubscribers
.slice()
.filter(sub => sub.before)
.forEach(sub => sub.before(action, this.state))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in before action subscribers: `)
console.error(e)
}
}
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
return new Promise((resolve, reject) => {
result.then(res => {
try {
this._actionSubscribers
.filter(sub => sub.after)
.forEach(sub => sub.after(action, this.state))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in after action subscribers: `)
console.error(e)
}
}
resolve(res)
}, error => {
try {
this._actionSubscribers
.filter(sub => sub.error)
.forEach(sub => sub.error(action, this.state, error))
} catch (e) {
if (__DEV__) {
console.warn(`[vuex] error in error action subscribers: `)
console.error(e)
}
}
reject(error)
})
})
}
辅助函数实现
mapState
/src/helper.js
export const mapState = normalizeNamespace((namespace, states) => {
const res = {};
if (__DEV__ && !isValidMap(states)) {
console.error("[vuex] mapState: mapper parameter must be either an Array or an Object");
}
console.log('namespace',namespace);
console.log('states',states);
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState() {
let state = this.$store.state;
let getters = this.$store.getters;
if (namespace) {
const module = getModuleByNamespace(this.$store, "mapState", namespace);
if (!module) {
return;
}
state = module.context.state;
getters = module.context.getters;
}
return typeof val === "function" ? val.call(this, state, getters) : state[val];
};
res[key].vuex = true;
});
return res;
});
mapGetters
/src/helper.js
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {};
if (__DEV__ && !isValidMap(getters)) {
console.error("[vuex] mapGetters: mapper parameter must be either an Array or an Object");
}
normalizeMap(getters).forEach(({ key, val }) => {
val = namespace + val;
res[key] = function mappedGetter() {
if (namespace && !getModuleByNamespace(this.$store, "mapGetters", namespace)) {
return;
}
if (__DEV__ && !(val in this.$store.getters)) {
console.error(`[vuex] unknown getter: ${val}`);
return;
}
return this.$store.getters[val];
};
res[key].vuex = true;
});
return res;
});
mapMutations
/src/helper.js
export const mapMutations = normalizeNamespace((namespace, mutations) => {
const res = {};
if (__DEV__ && !isValidMap(mutations)) {
console.error("[vuex] mapMutations: mapper parameter must be either an Array or an Object");
}
normalizeMap(mutations).forEach(({ key, val }) => {
res[key] = function mappedMutation(...args) {
let commit = this.$store.commit;
if (namespace) {
const module = getModuleByNamespace(this.$store, "mapMutations", namespace);
if (!module) {
return;
}
commit = module.context.commit;
}
return typeof val === "function" ? val.apply(this, [commit].concat(args)) : commit.apply(this.$store, [val].concat(args));
};
});
return res;
});
mapActions
/src/helper.js
export const mapActions = normalizeNamespace((namespace, actions) => {
const res = {};
if (__DEV__ && !isValidMap(actions)) {
console.error("[vuex] mapActions: mapper parameter must be either an Array or an Object");
}
normalizeMap(actions).forEach(({ key, val }) => {
res[key] = function mappedAction(...args) {
let dispatch = this.$store.dispatch;
if (namespace) {
const module = getModuleByNamespace(this.$store, "mapActions", namespace);
if (!module) {
return;
}
dispatch = module.context.dispatch;
}
return typeof val === "function" ? val.apply(this, [dispatch].concat(args)) : dispatch.apply(this.$store, [val].concat(args));
};
});
return res;
});
registerModule
/src/store.js
registerModule (path, rawModule, options = {}) {
if (typeof path === 'string') path = [path]
if (__DEV__) {
assert(Array.isArray(path), `module path must be a string or an Array.`)
assert(path.length > 0, 'cannot register the root module by using registerModule.')
}
this._modules.register(path, rawModule)
installModule(this, this.state, path, this._modules.get(path), options.preserveState)
resetStoreVM(this, this.state)
}
总结
commit 、 dispatch 实现原理
- 在 installModule 将对应的 mutation 、 action 添加到 _mutations 、 _actions 中。
- 调用 commit 执行就去 _mutations 中找到对应的 mutation 函数并执行。
- 调用 dispatch 执行就去 _actions 中找到对应的 action 函数并执行,而 action 中又是执行 commit 就是执行 mutation。
辅助函数的实现原理
- 通过解构辅助函数传入的参数得到对应命名空间和模块,返回对应的 state 、 getter 、 mutation 、 action。
怎么样区别 state 是否为 mutation 改变
- 通过 store._withCommit 方法,store 声明了 commiting 属性值为 boolean ,每次更改 state 前都通过 _withCommit 将其变更为 true ,当函数体执行完,再将其置为 false。如 state 改变而 commiting 为 false 说明不是规则允许的更改则抛错。
PS
- 代码仓库地址觉得对你有帮助记得 🌟 Star 哦
- 后续会持续更新相关内容以及其他内容
- 觉得文章不错的话,记得 👍 🌟 嘻嘻
- 如有不足欢迎指正