模擬vuex實(shí)現(xiàn)

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

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末呐芥,一起剝皮案震驚了整個(gè)濱河市志秃,隨后出現(xiàn)的幾起案子漠魏,更是在濱河造成了極大的恐慌柬脸,老刑警劉巖他去,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異倒堕,居然都是意外死亡灾测,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)垦巴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)媳搪,“玉大人,你說(shuō)我怎么就攤上這事骤宣∏乇” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵憔披,是天一觀的道長(zhǎng)鲜结。 經(jīng)常有香客問(wèn)我,道長(zhǎng)活逆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任拗胜,我火速辦了婚禮蔗候,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘埂软。我一直安慰自己锈遥,他們只是感情好纫事,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著所灸,像睡著了一般丽惶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爬立,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天钾唬,我揣著相機(jī)與錄音,去河邊找鬼侠驯。 笑死抡秆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吟策。 我是一名探鬼主播儒士,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼檩坚!你這毒婦竟也來(lái)了着撩?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤匾委,失蹤者是張志新(化名)和其女友劉穎拖叙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體剩檀,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡憋沿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沪猴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辐啄。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖运嗜,靈堂內(nèi)的尸體忽然破棺而出壶辜,到底是詐尸還是另有隱情,我是刑警寧澤担租,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布砸民,位于F島的核電站,受9級(jí)特大地震影響奋救,放射性物質(zhì)發(fā)生泄漏岭参。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一尝艘、第九天 我趴在偏房一處隱蔽的房頂上張望演侯。 院中可真熱鬧,春花似錦背亥、人聲如沸秒际。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)娄徊。三九已至闽颇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寄锐,已是汗流浹背兵多。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锐峭,地道東北人中鼠。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沿癞,于是被迫代替她去往敵國(guó)和親援雇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354