# 实现一个vuex
# 实现一个mini-vuex
vuex就不做讲解和,核心就是一种递归方法的使用,这里就直接贴代码记录一下好了
let Vue
const forEach = (obj, cb) => {
Object.keys(obj).forEach(key => {
cb(key, obj[key])
})
}
class ModuleCollection {
constructor(options) {
this.register([], options)
}
register(path, rootModule) {
let newModule = {
_raw: rootModule,
_children: {},
state: rootModule.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 (rootModule.modules) {
forEach(rootModule.modules, (moduleName, module) => {
this.register(path.concat(moduleName), module)
})
}
}
}
// 递归树 将结果挂载到 getters mutations actions
const installModule = (store, state, path, rootModule) => {
if (path.length > 0) { //如果是子模块 需要把子模块的状态放到父模块上
const parent = path.slice(0, -1).reduce((state, current) => {
return state[current]
}, state)
Vue.set(parent, path[path.length - 1], rootModule.state)
}
// 先处理根模块的getters属性
const getters = rootModule._raw.getters
if (getters) {
forEach(getters, (getterName, fn) => {
Object.defineProperty(store.getters, getterName, {
get: () => {
return fn(rootModule.state)
}
})
})
}
const mutations = rootModule._raw.mutations
if (mutations) {
forEach(mutations, (mutationName, fn) => {
let arr = store.mutations[mutationName] || (store.mutations[mutationName] = [])
arr.push((payload) => {
fn(rootModule.state, payload)
})
})
}
const actions = rootModule._raw.actions
if (actions) {
forEach(actions, (actionName, fn) => {
let arr = store.actions[actionName] || (store.actions[actionName] = [])
arr.push((payload) => {
fn(store, payload)
})
})
}
forEach(rootModule._children, (moduleName, module) => {
installModule(store, state, path.concat(moduleName), module)
})
}
class Store {
constructor(options) {
this._s = new Vue({
data: {
state: options.state // 把state变成可以监控的对象 state改变可以触发视图更新
}
})
this.getters = {}
this.mutations = {}
this.actions = {}
// 收集所有模块
this.modules = new ModuleCollection(options)
// 安装模块
installModule(this, this.state, [], this.modules.root)
}
commit = (type, payload) => {
this.mutations[type].forEach((fn) => {
fn(payload)
})
}
dispatch = (type, payload) => {
this.actions[type].forEach((fn) => {
fn(payload)
})
}
get state() {
return this._s.state
}
}
const 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 {
install,
Store
}