既然 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, }
完啦,謝謝你們觀看學習!哈哈哈哈數組