Vuex是基於Vue的響應式原理基礎,所以無法拿出來單獨使用,必須在Vue的基礎之上使用。
1.Vuex使用相關解析
1 import store form './store' // 引入一個store文件 2 3 new Vue({ 4 // 在Vue初始化的過程中,注入一個store屬性,內部會將這個屬性放到每個組件的$store上 5 store, 6 })
store.js
1 import Vuex from 'Vuex' 2 3 Vue.use(Vuex) 4 5 // 通過Vuex中的一個屬性 Store 創建一個store的實例 6 export default new Vuex.Store({ 7 state: { // 單一數據源 8 age: 10 9 }, 10 mutations: { // 11 // payload 載荷 12 syncChange(state,payload) { // 修改狀態的方法 同步更改 13 state.age+= payload 14 } 15 }, 16 actions: { 17 asyncChange({commit}, payload) { 18 setTimeout(() => { 19 commit('syncChange',payload) 20 },1000) 21 } 22 } 23 }) 24 //mutations中增加異步操作 嚴格模式下會直接報錯,普通模式下不會報錯但不合法
2.Vuex原理解析實現
首先我們要清楚Vuex的定位,它是一個插件。且必須基於之上Vue來使用,為什么這么說呢,因為他的數據響應是基於Vue的。
1.Vuex核心概念
① state 驅動應用的數據源。
② Getter getter的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生變化了改變才會被重新計算(由此你是不是想到了計算屬性,對完全可以這么理解)。
③ Mutation 進行Vuex中store狀態的更改,也是官方規定更改狀態的唯一途徑。
④ Action 進行異步操作的場所,但是更改數據還是需要commit提交。
⑤ Module 單一狀態樹對象比較復雜,Vuex允許我們將Store分割成多模塊,每個模塊擁有自己獨立的內容。
2.實現Vuex
store.js
先創建一個入口文件
1 import Vue from 'vue' 2 // import Vuex from 'vuex' 官方的Vuex插件 3 import Vuex from './vuex/index1' // 自己寫的Vuex 4 5 Vue.use(Vuex) // 默認會執行當前插件的install方法 6 7 // 通過Vuex中的一個屬性 Store 創建一個store的實例 8 export default new Vuex.Store({ 9 // 定義數據 10 modules: { 11 a: { 12 state: { 13 age: 'a100' 14 }, 15 mutations: { 16 syncChange() { 17 console.log('a'); 18 } 19 }, 20 }, 21 b: { 22 state: { 23 age: 'b100' 24 }, 25 mutations: { 26 syncChange() { 27 console.log('b'); 28 } 29 }, 30 modules: { 31 c: { 32 state: { 33 age: 'c100' 34 }, 35 mutations: { 36 syncChange() { 37 console.log('c'); 38 } 39 }, 40 } 41 } 42 } 43 }, 44 state: { 45 age: 10 46 }, 47 mutations: { 48 syncChange(state, payload) { 49 state.age += payload 50 } 51 }, 52 actions: { 53 asyncChange({ commit }, payload) { 54 setTimeout(() => { 55 commit('syncChange', payload) 56 }, 1000) 57 } 58 }, 59 getters: { 60 myAge(state) { 61 return state.age + 20 62 } 63 } 64 })
index1.js
這邊會暴露出一個install方法,Vue.sue()的時候會調用它。還有一個提供實例化的Store類
1 let vue 2 const install = (_Vue) => { 3 Vue = _Vue // install方法調用時,會將Vue作為參數傳入 4 5 Vue.mixin({ // 全局注冊一個混入,影響注冊以后的每一個創建的Vue實例。 6 beforeCreate() { 7 // 判斷當前根實例上有沒有store,有的話把根組件的的store屬性 放到每個組件的實例上 8 // 這樣每個組件上都能直接實現this.$store去訪問store里面的東西 9 if(this.$option.store) { 10 this.$store = this.$options.store 11 } else { 12 this.$store = this.$parent && this.$parent.$store 13 } 14 } 15 }) 16 } 17 18 19 class Store { // 用戶獲取的是這個store類的實例 20 constructor(options) { 21 // 創建Vue的實例 保證更新狀態可以刷新視圖 22 this.vm = new Vue({ 23 data: { 24 state: optons.state 25 } 26 }) 27 } 28 29 // es6 的訪問器 30 get state() { 31 return this.vm.state 32 } 33 34 this.getters = {} 35 this.mutations = {} 36 this.actions = {} 37 // 1、需要將用戶傳入的數據進行格式化操作 38 this.moudules = new ModulesCollections(options) 39 40 // 2、遞歸的安裝模塊 41 installModule(this,this.state,[], this.modules.root) 42 43 // 調用 44 commit = (mutationName, payload) => { 45 // es7寫法 這個里面的this 永遠指向當前的store實例 46 this.mutaions[mutationName].forEach(fn =>fn(payload)) 47 } 48 49 dispath = (actionName, payload) => { 50 this.actions[actionName].forEach(fn =>fn(payload)) 51 } 52 } 53 54 // 定義一個forEach遍歷當前對象屬性然后執行一個回調函數,后面要用到 55 const forEach = (obj, callback) => { 56 Object.keys(obj).forEach(key => { 57 callback(key, obj[key]) 58 }) 59 } 60 61 // 格式化用戶數據 62 class ModuleCollection { 63 constructor(options) { 64 // 深度將所有的子模塊都遍歷一遍 65 this.register([], ooptions) 66 } 67 register(path, rootModule) { 68 let rawModule = { 69 _raw: rootModule, 70 _children: {}, 71 state: rootModule.state 72 } 73 if(!this.root) { 74 this.root = rawModule 75 } else { 76 // 找到要定義的模塊,將這個模塊定義他父親的_children屬性里 77 let parentModule = path.slice(0,-1).reduce((root, current) => { 78 return root._children[current] 79 }, this.root) 80 parentModule._childen[path[path.length - 1]] = rawModule 81 } 82 83 // 如果有子模塊 84 if(rootModule.modules) { 85 forEach(rootModule.modules,(moduleName, module) => { 86 this.register(path.concat(moduleName), module) 87 }) 88 } 89 } 90 } 91 92 // 遞歸安裝模塊 93 function installModule(store, rootState, path, rawModeule) { 94 // 如果有子模塊,安裝子模塊的狀態 95 if(path.length > 0) { 96 let parentState = path.slice(0,-1).reduce((root, current) => { 97 return root[current] 98 }, rootState) 99 Vue.set(parentState, path[path.length -1],rawModule.state) 100 } 101 102 let getters = rawModule._raw.getters // 取用戶的getter 103 if(getters) { 104 forEach(getters, (getterName, value) => { 105 Object.defineProperty(store.getters, getterName, { 106 get: () => { 107 return value(rawModule.state) 108 } 109 }) 110 }) 111 } 112 113 let mutations = rawModule.raw.mutations // 取用戶的mutation 114 if(mutations) { 115 forEach(mutations, (mutationName, value) => { 116 let arr = store.mutations[mutationName] || (store.mutaons[mutationName] = []) 117 }) 118 arr.push((plyload) => { 119 value(rawModule.state, payload) 120 }) 121 } 122 123 let actions = rawModule._raw.actions // 取用戶的action 124 if(actions) { 125 forEach(actions, (actionName, value) => { 126 let arr = store.actions[actionName] || (store.actions[actionName] = []) 127 arr.push((payload) => { 128 value(store, payload) 129 }) 130 }) 131 } 132 133 // 遞歸 134 forEach(rawModule._childen, (moduleName, rawModule) => { 135 installModule(store, rootState, path.concat(moduleName),rawModule) 136 }) 137 }
3.實現步驟總結:
1、作為插件引入,執行install方法調用Vue.mixin在Vue全局生命周期混入一個方法,將Vuex中定義的數據源掛載到this.$store,即當前組件的實例上。
2、state 直接new Vue實例,將數據源傳入。完成數據源響應式操作。
3、getters 遞歸遍歷用戶傳入的getters對象,拿到每個里面每一個函數,通過Object.definedProperty屬性處理。當get函數讀取compile,觸發get調用相應函數(函數內部自動傳入當前數據源state作為參數),完成數據響應。
4、mutations 遞歸遍歷用戶傳入的mutations 對象,將相同名稱下的函數都掛載到當前實例的mutations數組中,完成訂閱。commit的時候拿到對應的函數名稱進行遍歷mutations數組調用對應名稱函數,完成發布。
5、actiosns 操作和mutations一樣。
6、module 是將用戶傳入的數據進行格式化,格式化好以后執行上面的安裝模塊的方法。具體查看上方installModule方法的詳細操作。