既然 VUEX 能够 use,内部一定是有一个 install 方法,因此咱们先要实现一个install方法,当咱们用的时候,每个组件上面都有一个this.$store属性,里面包含了状态仓库里面的state,mutations, actions, getters,因此咱们也须要在每一个组件上都挂载一个$store属性,具体实现以下:
let Vue = null; export function install(_Vue) { // 为了防止重复注册 if (Vue !== _Vue) { Vue = _Vue } Vue.mixin({ beforeCreate() { const options = this.$options; if (options.store) { this.$store = options.store; } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store; } } }) }
为了后面的缓缓,咱们须要封装一个本身的循环方法,用来简化代码操做
const forEach = (obj, cb) => { Object.keys(obj).forEach(key => { cb(key, obj[key]); }) }
到此咱们就能够正常的引入并use(vuex)啦,可是此时咱们并无去初始化仓库,由于原声的vuex还须要去 new Vuex.Store(),能够传入一些初始化参数,好比state、mutations、actions、getters,既然能 new ,说明这是一个 类,因此咱们如今去写 Store 这个类,具体实现以下:
export class Store { constructor(options = {}) { // TODO.... } }
好了,这个时候页面就不会报错啦,并且也能够经过 this.$store.state.xxx 取到state中的值了,可是原生的state从新设置会引起视图更新,因此还须要把store中的state设置成响应式的,具体实现以下:
export class Store { constructor(options = {}) { this.vm = new Vue({ data(){ return { state: options.state } } }) }, // 类的属性访问器 get state() { return this.vm.state } }
到此咱们在页面中设置值就能够引起视图更新啦,由于咱们已经把全部的数据变成了双向绑定,只要视图更改就会引起视图更新这个时候,咱们在初始化store的时候还传入了mutations、actions、getters,这些数据都尚未处理,因此如今咱们就能够优先处理这些数据,并且在$store属性上还有 commit,dispatch,各类属性,因此咱们还须要把这些属性页所有写好,具体实现以下:javascript
export class Store { constructor(options = {}) { this.vm = new Vue({ data(){ return { state: options.state } } }) this.getters = {}; this.mutations = {}; this.actions = {}; // 处理 getters 响应式数据 let getters = options.getters; forEach(getters,(getterName, fn)=>{ Object.defineProperty(this.getters, getterName, { get: () => { return fn(this.state) } }) }) // 获取全部的同步的更新操做方法 let mutations = options.mutations; forEach(mutations,(mutationName, fn) => { this.mutations[mutationName] = (payload) => { fn(this.state, payload) } }); // 获取全部的异步的更新操做方法 let actions = options.actions; forEach(actions,(actionName, fn) => { this.actions[actionName] = (payload) => { fn(this, payload); } }) } commit = (type, payload) => { this.mutations[type](payload) } dispatch = (type, payload) => { this.actions[type](payload) } get state() { return this.vm.state } }
到此,基本的vuex 已经能够正常运行啦!是否是很简单!!!!!vue
可是,到此还远远不止!!!!!!java
咱们知道,原生的 vuex 中还有modules 属性,里面能够嵌套任意层state,mutations,actions,getters,因此咱们还须要处理这个属性
看原生的vuex属性中有个_modules 属性,是一个树结构
因此咱们也仿照原生,生成一个__modules树结构的对象
// 格式化 _modules class ModuleCollection { constructor(options) { // 注册模块 将模块注册成树结构 this.register([], options); } register(path, rootModule) { let module = { // 将模块格式化 _rawModule: rootModule, _chidlren: {}, state: rootModule.state } if (path.length == 0) { // 若是是根模块 将这个模块挂在到根实例上 this.root = module; } else { // 递归都用reduce方法 // 经过 _children 属性进行查找 let parent = path.slice(0, -1).reduce((root, current) => { return root._chidlren[current] }, this.root) parent._chidlren[path[path.length - 1]] = module } // 看当前模块是否有modules , 若是有modules 开始从新再次注册 if (rootModule.modules) { forEach(rootModule.modules, (moduleName, module) => { this.register(path.concat(moduleName), module) }) } } }
而后在 Store 类中把 _modules 属性挂载到实例上
Store类中: // 把数据格式化成一个 想要的树结构 this._modules = new ModuleCollection(options);
到此,咱们把modules都已经处理成了想要的结构,可是各个模块下的属性都没有挂载处理,因此咱们还须要递归挂载各个模块的属性,因此上面写的处理mutations、atcions、getters属性的方法都须要从新去写,具体实现以下:
/** * @explain { 安装模块 } * @param { store } 整个store * @param { rootState } 当前的根状态 * @param { path } 为了递归来建立的 * @param { rootModule } 从根模块开始安装 */ const installModule = (store, rootState, path, rootModule) => { if (path.length > 0) { // [a] // 是儿子,儿子要找到爸爸将本身的状态 放到上面去 let parent = path.slice(0, -1).reduce((root, current) => { return root[current] }, rootState) // vue 不能在对象上增长不存在的属性 不然不会致使视图更新 Vue.set(parent, path[path.length - 1], rootModule.state); // {age:1,a:{a:1}} // 实现了 查找挂在数据格式 } // 如下代码都是在处理 模块中 getters actions mutation let getters = rootModule._rawModule.getters; if (getters) { forEach(getters, (getterName, fn) => { Object.defineProperty(store.getters, getterName, { get() { return fn(rootModule.state); // 让对应的函数执行 } }); }) } let mutations = rootModule._rawModule.mutations; if (mutations) { forEach(mutations, (mutationName, fn) => { let mutations = store.mutations[mutationName] || []; mutations.push((payload) => { fn(rootModule.state, payload); // 发布 让全部的订阅依次执行 store._subscribes.forEach(fn => fn({ type: mutationName, payload }, rootState)); }) store.mutations[mutationName] = mutations; }) } let actions = rootModule._rawModule.actions; if (actions) { forEach(actions, (actionName, fn) => { let actions = store.actions[actionName] || []; actions.push((payload) => { fn(store, payload); }) store.actions[actionName] = actions; }) } // 挂载儿子 forEach(rootModule._chidlren, (moduleName, module) => { installModule(store, rootState, path.concat(moduleName), module) }) }
而后在 Store 类中执行此处理函数,完胜挂载
/** * @explain { 安装模块 } * @param { this } 整个store * @param { this.state } 当前的根状态 * @param { [] } 为了递归来建立的 * @param { this._modules.root } 从根模块开始安装 */ installModule(this, this.state, [], this._modules.root);
此时,咱们本身写的 vuex 就能够彻底正常运行啦,能够随意书写modules,state,mutations,actions,getters,是否是很酷!!!!!!哈哈哈哈可是咱们还有一个 plugins 属性还没实现,具体这块的实现很是简单,由于咱们在使用 plugins 的时候,传递一个数组,数组里面是一个个的 中间件函数,每个函数的第一个参数都是 仓库实例自己,因此,咱们在代码里就能够这样写vuex
// 处理 插件 (options.plugins || []).forEach(plugin => plugin(this));
到此,咱们的 vuex 就实现啦,是否是很简单,哈哈哈哈哈哈哈
const forEach = (obj, cb) => { Object.keys(obj).forEach(key => { cb(key, obj[key]); }) } let Vue = null; export function install(_Vue) { if (Vue !== _Vue) { Vue = _Vue } Vue.mixin({ beforeCreate() { const options = this.$options; if (options.store) { this.$store = options.store; } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store; } } }) } /** * @explain { 安装模块 } * @param { store } 整个store * @param { rootState } 当前的根状态 * @param { path } 为了递归来建立的 * @param { rootModule } 从根模块开始安装 */ const installModule = (store, rootState, path, rootModule) => { if (path.length > 0) { // [a] // 是儿子,儿子要找到爸爸将本身的状态 放到上面去 let parent = path.slice(0, -1).reduce((root, current) => { return root[current] }, rootState) // vue 不能在对象上增长不存在的属性 不然不会致使视图更新 Vue.set(parent, path[path.length - 1], rootModule.state); // {age:1,a:{a:1}} // 实现了 查找挂在数据格式 } // 如下代码都是在处理 模块中 getters actions mutation let getters = rootModule._rawModule.getters; if (getters) { forEach(getters, (getterName, fn) => { Object.defineProperty(store.getters, getterName, { get() { return fn(rootModule.state); // 让对应的函数执行 } }); }) } let mutations = rootModule._rawModule.mutations; if (mutations) { forEach(mutations, (mutationName, fn) => { let mutations = store.mutations[mutationName] || []; mutations.push((payload) => { fn(rootModule.state, payload); // 发布 让全部的订阅依次执行 store._subscribes.forEach(fn => fn({ type: mutationName, payload }, rootState)); }) store.mutations[mutationName] = mutations; }) } let actions = rootModule._rawModule.actions; if (actions) { forEach(actions, (actionName, fn) => { let actions = store.actions[actionName] || []; actions.push((payload) => { fn(store, payload); }) store.actions[actionName] = actions; }) } // 挂载儿子 forEach(rootModule._chidlren, (moduleName, module) => { installModule(store, rootState, path.concat(moduleName), module) }) } class ModuleCollection { // 格式化 constructor(options) { // 注册模块 将模块注册成树结构 this.register([], options); } register(path, rootModule) { let module = { // 将模块格式化 _rawModule: rootModule, _chidlren: {}, state: rootModule.state } if (path.length == 0) { // 若是是根模块 将这个模块挂在到根实例上 this.root = module; } else { // 递归都用reduce方法 // 经过 _children 属性进行查找 let parent = path.slice(0, -1).reduce((root, current) => { return root._chidlren[current] }, this.root) parent._chidlren[path[path.length - 1]] = module } // 看当前模块是否有modules , 若是有modules 开始从新再次注册 if (rootModule.modules) { forEach(rootModule.modules, (moduleName, module) => { this.register(path.concat(moduleName), module) }) } } } export class Store { constructor(options = {}) { this.vm = new Vue({ data(){ return { state: options.state } } }) this.getters = {}; this.mutations = {}; this.actions = {}; this._subscribes = []; // 把数据格式化成一个 想要的树结构 this._modules = new ModuleCollection(options); /** * @explain { 安装模块 } * @param { this } 整个store * @param { this.state } 当前的根状态 * @param { [] } 为了递归来建立的 * @param { this._modules.root } 从根模块开始安装 */ installModule(this, this.state, [], this._modules.root); // 处理 插件 (options.plugins || []).forEach(plugin => plugin(this)); } subscribe(fn){ this._subscribes.push(fn); } commit = (type, payload) => { this.mutations[type].forEach(cb => cb(payload)) } dispatch = (type, payload) => { this.actions[type].forEach(cb => cb(payload)) } get state() { return this.vm.state } } export default { install, Store, }
完啦,谢谢你们观看学习!哈哈哈哈数组