modules 結(jié)構(gòu)如下:
modules: {
a: {
namespaced: true,
state: {
age: 'a1',
},
mutations: {
syncChange() {
console.log('a-syncChange')
}
}
},
b: {
namespaced: true,
state: {
age: 'b1',
},
mutations: {
syncChange() {
console.log('b-syncChange')
}
},
modules: {
c: {
namespaced: true,
state: {
age: 'c1'
},
mutations: {
syncChange() {
console.log('c-syncChange')
}
}
}
}
}
}
modules有2個(gè)子模塊 a模塊和b模塊,其中b模塊有子模塊c惧辈。
let Vue;//存放 vue
let forEach = (obj, callback) => {//循環(huán)對(duì)象
Object.keys(obj).forEach(key => {
callback(key, obj[key]);
})
}
class ModuleCollection {
constructor(options) {
//深度遍歷子模塊
this.register([], options);
}
register(path, rootModule) {
let rawModule = {
_raw: rootModule,//模塊內(nèi)容
_children: {},//子模塊
state: rootModule.state//數(shù)據(jù)
}
rootModule.rawModule = rawModule // 雙向記錄 在用戶(hù)傳入的對(duì)象中記錄下 自己的模塊
if (!this.root) {
this.root = rawModule //根模塊
} else {
// 不停的找到要定義的模塊 將這個(gè)模塊定義到他的父親上
let parentModule = path.slice(0, -1).reduce((root, current) => {
return root._children[current];
}, this.root);
parentModule._children[path[path.length - 1]] = rawModule
}
if (rootModule.modules) {//判斷是夠有modules 有就遞歸執(zhí)行
forEach(rootModule.modules, (moduleName, module) => {
// 將a模塊進(jìn)行注冊(cè) [a], a模塊的定義
// 將b模塊進(jìn)行注冊(cè) [b], b模塊的定義
// 將c模塊進(jìn)行注冊(cè) [b,c], c模塊的定義
this.register(path.concat(moduleName), module)
})
}
}
}
function getState(store, path) { // [a]
let local = path.reduce((newState, current) => {
return newState[current]; // 每次調(diào)用mutation的時(shí)候傳入的參數(shù)我都保證他是最新獲取到的,而不是默認(rèn)安裝時(shí)的數(shù)據(jù)
}, store.state);
return local
}
function installModule(store, rootState, path, rawModule) {
let getters = rawModule._raw.getters;
// 根據(jù)當(dāng)前用戶(hù)傳入的配置 算一下他需不需要增加一個(gè)前綴
let root = store.modules.root // 獲取到了最終整個(gè)的格式化的結(jié)果
// [b,c] => b/c
// [a] => a
let namespace = path.reduce((str, current) => {
// 這個(gè)root是整個(gè)格式化的
// root._children[current] 拿到的是當(dāng)前通過(guò)路徑獲取到的模塊
root = root._children[current]; // 拿到對(duì)應(yīng)的格式化的結(jié)果
str = str + (root._raw.namespaced ? current + '/' : '');
return str; // a/b/
}, '');
if (path.length > 0) { // 兒子模塊
let parentState = path.slice(0, -1).reduce((root, current) => {
return rootState[current]
}, rootState)
Vue.set(parentState, path[path.length - 1], rawModule.state);
}
if (getters) {
forEach(getters, (getterName, value) => {
Object.defineProperty(store.getters, namespace + getterName, {
get: () => {
return value(getState(store, path))
}
})
})
}
let mutations = rawModule._raw.mutations;
if (mutations) {
forEach(mutations, (mutationName, value) => {
let arr = store.mutations[namespace + mutationName] || (store.mutations[namespace + mutationName] = []);
arr.push((payload) => {
value(getState(store, path), payload); // 真正執(zhí)行mutation的地方
store.subs.forEach(fn => fn({
type: namespace + mutationName,
payload: payload
}, store.state));
})
})
}
let actions = rawModule._raw.actions;
if (actions) {
forEach(actions, (actionName, value) => {
let arr = store.actions[namespace + actionName] || (store.actions[namespace + actionName] = []);
arr.push((payload) => {
value(store, payload)
})
})
}
forEach(rawModule._children, (moduleName, rawModule) => {
installModule(store, rootState, path.concat(moduleName), rawModule)
})
}
class Store {
constructor(options) {//options 獲取用戶(hù)new實(shí)例時(shí)的所有屬性
this.strict = options.strict || false;//是否為嚴(yán)格模式
this._committing = false; // 默認(rèn)是沒(méi)有提交
this.vm = new Vue({//創(chuàng)建vue實(shí)例 使劫持?jǐn)?shù)據(jù) 變?yōu)轫憫?yīng)式
data: {
state: options.state
}
});
this.getters = {};
this.mutations = {};
this.actions = {};
this.subs = [];
// 1.我需要將用戶(hù)傳入的數(shù)據(jù)進(jìn)行格式化操作
this.modules = new ModuleCollection(options) //格式化我們想要的數(shù)據(jù)結(jié)構(gòu)
// 2.遞歸的安裝模塊 store/rootState/path/根模塊
installModule(this, this.state, [], this.modules.root);
let plugins = options.plugins;
plugins.forEach(plugin => plugin(this));
if(this.strict){
this.vm.$watch(()=>{
return this.vm.state
},function () {
console.assert(this._committing,'不能異步調(diào)用')
// 希望他可以深度監(jiān)控 (異步執(zhí)行的)
},{deep:true,sync:true}); // 監(jiān)控了是否采用同步的方式更改了數(shù)據(jù)
}
}
_withCommit(fn){
const committing = this._committing; // 保留false
this._committing = true; // 默認(rèn)調(diào)用mutation之前會(huì)先 更改值是true
fn();
this._committing = committing
}
replaceState(newState) {
this._withCommit(()=>{
this.vm.state = newState; // 更新?tīng)顟B(tài)
})
}
subscribe(fn) {
this.subs.push(fn);
}
commit = (mutationName, payload) => {
this._withCommit(()=>{ // 裝飾 切片
this.mutations[mutationName].forEach(fn => fn(payload));
})
}
dispatch = (actionName, payload) => {
this.actions[actionName].forEach(fn => fn(payload));
}
get state() {
return this.vm.state
}
registerModule(moduleName, module) {
this._committing = true
if (!Array.isArray(moduleName)) {
moduleName = [moduleName]
}
this.modules.register(moduleName, module); // 添加到我們自己格式化的樹(shù)中了
// 將當(dāng)前這個(gè)模塊進(jìn)行安裝 // [d], {_raw,_children,state}
// 只安裝當(dāng)前的木塊
installModule(this, this.state, moduleName, module.rawModule);
}
}
// 官方api
const install = (_Vue) => { //_vue是 Vue的構(gòu)造函數(shù)
Vue = _Vue;
Vue.mixin({//抽離公共邏輯 放一些方法
//從根實(shí)例開(kāi)始 所有的子組件才有$store 方法
//組件創(chuàng)建的過(guò)程 是先父后子
beforeCreate() {
if (this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export const mapState = (stateArr) => { // {age:fn}
let obj = {};
stateArr.forEach(stateName => {
obj[stateName] = function () {
return this.$store.state[stateName]
}
});
return obj;
}
export function mapGetters(gettersArr) {
let obj = {};
gettersArr.forEach(getterName => {
obj[getterName] = function () {
return this.$store.getters[getterName];
}
});
return obj
}
export function mapMutations(obj) {
let res = {};
Object.entries(obj).forEach(([key, value]) => {
res[key] = function (...args) {
this.$store.commit(value, ...args)
}
})
return res;
}
export function mapActions(obj) {
let res = {};
Object.entries(obj).forEach(([key, value]) => {
res[key] = function (...args) {
this.$store.dispatch(value, ...args)
}
})
return res;
}
export default {
Store,
install
}