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方法的詳細操作。