English
100 行写一个 vuex
既然想 100 行拿下 vuex,思考一下 vuex 核心之核心的逻辑是什么?
Vuex 作为 Vue 插件,所以需要 install
全部代码实现:
js
let Vue;
class ModuleCollection {
constructor(options) {
this.register([], options);
}
register(path, rawModule) {
let newModule = {
_raw: rawModule,
_children: {},
state: rawModule.state
}
if (path.length == 0) {
this.root = newModule;
} else {
let parent = path.slice(0, -1).reduce((root, current) => {
return root._children[current];
}, this.root);
parent._children[path[path.length - 1]] = newModule;
}
if (rawModule.modules) { // 有子模块
forEach(rawModule.modules, (childName, module) => {
// [a,b];
// [a,d]
this.register(path.concat(childName), module)
});
}
}
}
// vuex 中间件 可以封装自己的逻辑 subscirbe registerModule unregisterModule
function installModule(store, rootState, path, rootModule) {
// rootState.a = {count:200}
// rootState.a.b = {count:3000}
if (path.length > 0) { // [a,b]
// 第二次 获取到的就是a对应的对象
let parent = path.slice(0, -1).reduce((root, current) => {
return root[current];
}, rootState)
// {count:1000,a:{}}
Vue.set(parent, path[path.length - 1], rootModule.state);
}
if (rootModule._raw.getters) {
forEach(rootModule._raw.getters, (getterName, getterFn) => {
Object.defineProperty(store.getters, getterName, {
get: () => {
return getterFn(rootModule.state);
}
});
});
}
if (rootModule._raw.actions) {
forEach(rootModule._raw.actions, (actionName, actionFn) => {
let entry = store.actions[actionName] || (store.actions[actionName] = []);
entry.push(() => {
actionFn.call(store, store);
})
});
}
if (rootModule._raw.mutations) {
forEach(rootModule._raw.mutations, (mutationName, mutationFn) => {
let entry = store.mutations[mutationName] || (store.mutations[mutationName] = []);
entry.push(() => {
mutationFn.call(store, rootModule.state);
})
});
}
forEach(rootModule._children, (childName, module) => {
installModule(store, rootState, path.concat(childName), module);
})
}
class Store {
constructor(options) {
let state = options.state;
this.getters = {};
this.mutations = {};
this.actions = {}
this._vm = new Vue({
data: {
state
}
});
this.modules = new ModuleCollection(options);
installModule(this, state, [], this.modules.root);
let { commit, dispatch } = this;
this.commit = (type) => {
commit.call(this, type);
}
this.dispatch = (type) => {
dispatch.call(this, type);
}
}
get state() {
return this._vm.state;
}
commit(type) {
this.mutations[type].forEach(fn => fn());
}
dispatch(type) {
this.actions[type].forEach(fn => fn());
}
}
function forEach(obj, callback) {
Object.keys(obj).forEach(item => callback(item, obj[item]));
}
let install = (_Vue) => {
Vue = _Vue;
Vue.mixin({
beforeCreate() {
if (this.$options && this.$options.store) {
this.$store = this.$options.store;
} else { // 子组件 深度优先 父-> 子 -> 孙子
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
Store,
install
}