1. 在写VUEX之前先了解VUE中组件的注册
- 在调用vue.use()会调用组件的install方法
- Vue.mixin()会在全局的vue实例中都会获取到
先了解VUEX的基本使用方法 vuex.vuejs.org/zh/
2. 文件结构
vuex
----index.js
----mixin.js
----store.js
----util.js
3. 代码实现
index.js
import { Store, install } from "./store";
export default {
Store,
install
};
store.js
import applyMixin from "./mixin";
import { ModuleCollecton, installModule } from "./util";
export let Vue;
class Store {
constructor(options) {
const state = options.state;
this._vm = new Vue({
data: {
state
}
});
// 处理getter
this.getters = {};
// forEachValue(options.getters || {}, (fn, key) => {
// Object.defineProperty(this.getters, key, {
// get: () => { console.log('我变了'); return fn(this.state);}
// });
// });
// 处理mutation
this.mutations = {};
// forEachValue(options.mutations || {}, (fn, key) => {
// this.mutations[key] = (payload) => {
// fn(this.state, payload)
// }
// })
// 处理actions
// 最后会做一个监控 看看是不是异步方法都在action更新 不是在mutation执行的
this.actions = {};
// forEachValue(options.actions || {}, (fn, key) => {
// this.actions[key] = (payload) => {
// fn(this, payload)
// }
// })
// ------------------------------------------------------------------
// 想成为这种格式
// let root = {
// _row: rootModule,
// state: rootModule.state,
// _children: {
// a: {
// _row: aModule,
// state: aModule.state,
// _children: {},
// },
// b: {
// _row: bModule,
// state: bModule.state,
// _children: {
// c: {
// _row: cModule,
// state: cModule.state,
// _children: {}
// }
// }
// }
// }
// }
// 格式化
this.modules = new ModuleCollecton(options)
console.log(this.modules);
// 递归的安装模块 比如action mutation getters
// 对该vue实例state 进行安装
// 把上面的变为
// 数据拍平
// this.getters = {
// a: (stateA) => {},
// b: (stateB) => {},
// c: (stateC) => {},
// };
// 数据拍平
// this.mutations = {
// a: [(stateA) => {}, (stateAa) => {}],
// b: [(stateB) => {}, (stateBa) => {}],
// c: [(stateC) => {}, (stateCc) => {}],
// };
// 形成属性结构 注意这里必须使用$set方式 否则无法响应数据
// state = {
// a: {
// age: '123',
// b: {
// age: '345'
// }
// }
// }
installModule(this, this.state, [], this.modules.root)
}
// 处理state
get state() {
return this._vm.state;
}
// 处理commit
commit = (mutationName, payload) => { // this指向
this.mutations[mutationName].forEach(fn => {
fn(payload)
});
}
// 处理dispatch
dispatch = (actionName, payload) => {
this.actions[actionName].forEach(fn => {
fn(payload)
})
}
// 动态注册
registerModule = (moduleName, module) => {
if (!Array.isArray(moduleName)) {
moduleName = [moduleName]
}
this.modules.register(moduleName, module) // 只进行了数据格式化
console.log(this.modules);
installModule(this, this.state, moduleName, this.modules.root)
}
}
// vue的构造函数
export function install(_vue) {
Vue = _vue;
applyMixin(Vue);
}
export {Store}
util.js
import {Vue} from './store'
export const forEachValue = (obj, callback) => {
Object.keys(obj).forEach(key => callback(obj[key], key));
};
export class ModuleCollecton {
constructor(options) {
// 深度遍历 进行模块集合
this.register([], options)
}
register(path, rootModule) {
let rowModule = {
_row: rootModule,
state: rootModule.state,
_children: {}
}
if (!this.root) {
this.root = rowModule
} else {
let parentModule = path.slice(0, -1).reduce((pre, cur) => {
return pre._children[cur]
}, this.root)
parentModule._children[path[path.length - 1]] = rowModule
}
if (rootModule.modules) {
forEachValue(rootModule.modules, (moduleVal, moduleName) => {
this.register(path.concat(moduleName), moduleVal)
})
}
}
}
export function installModule(store, rootState, path, rowModule) { // _row, _children, state
let getters = rowModule._row.getters
if(getters) {
// installmodule第二次会重复 但是这个方法不可以
forEachValue(getters, (val, getterName) => {
if (!getters[getterName]) {
Object.defineProperty(store.getters, getterName, {
get: () => {
return val(rowModule.state) // 模块中的状态
}
})
}
})
}
let mutations = rowModule._row.mutations
if(mutations) {
forEachValue(mutations, (val, mutationName) => {
let arr = store.mutations[mutationName] || (store.mutations[mutationName] = [])
arr.push((payload) => {
val(rowModule.state, payload)
})
})
}
let actions = rowModule._row.actions
if(actions) {
forEachValue(actions, (val, actionName) => {
let arr = store.actions[actionName] || (store.actions[actionName] = [])
arr.push((payload) => {
val(store, payload)
})
})
}
let state = rowModule._row.state
if (state) {
if(path.length > 0) { // 有子模块
// Vue的响应式原理 不能增加不存在的属性
let parentState = path.slice(0, -1).reduce((pre, cur) => {
return pre[cur]
}, rootState)
Vue.set(parentState, path[path.length - 1], rowModule.state)
}
}
forEachValue(rowModule._children, (module, moduleName) => {
installModule(store, rootState, path.concat(moduleName), module)
})
}
mixin.js
export default function applyMixin(Vue) {
console.log(Vue);
Vue.mixin({
beforeCreate: vuexInit
});
}
// 这个生命周期会提前执行 优先于子组件的生命周期
function vuexInit() {
const options = this.$options;
// 根实例
if (options.store) {
this.$store = options.store;
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store;
}
}