vuex 版本爲
^2.3.1
,按照我本身的理解來整理vuex。
每一個vuex 應用只有一個 store 實例,因此使用起來不會太複雜,對於定位錯誤狀態和操做會很方便。vue
最基本的使用方式,經過在vue文件裏面初始化 vuex 的 store 來進行操做 vuex 的數據:以下例子:git
// 在組件裏面使用 vuex // 初始化 vuex 實例 const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment: state => state.count++, decrement: state => state.count-- } }) // 建立一個 Counter 組件 const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { // 直接返回 state return store.state.count } } } // 初始化 vue 實例 const app = new Vue({ el: '#app', components: { Counter }, template: ` <div class="app"> <button @click="increment">+</button> <button @click="decrement">-</button> <counter></counter> </div> ` , methods: { increment () { store.commit('increment') }, decrement () { store.commit('decrement') } } })
store.state.count
變化的時候, 都會從新求取計算屬性,而且觸發更新相關聯的 DOM。我覺得,當項目發展到多個模塊,多個組件和子組件混合的時候,在這種場合,單純的值傳遞方式會很麻煩,由於組件或者模塊之間的變量是獨立的,對於一些全局使用的屬性相似 token,cookie 之類的東西,或者是一些多個模塊之間共享的屬性。
因此 vuex 提供一個新的方式來將 vuex 的 store 存放到根組件下,經過 store 選項,將store從根組件「注入」到每個子組件中(需調用 Vue.use(Vuex)
):es6
// 初始化 vuex的 store(能夠將這個放到其餘文件裏面去) const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment: state => state.count++, decrement: state => state.count-- } }) // 在初始化 vue的時候註冊 store(store 便是 vuex 的 store) const app = new Vue({ el: '#app', // 把 store 對象提供給 「store」 選項,這能夠把 store 的實例注入全部的子組件 store, components: { Counter }, // 子組件 template: ` <div class="app"> <counter></counter> </div> ` })
經過在根實例中註冊 store 選項,該 store 實例會注入到根組件下的全部子組件中,且子組件能經過 this.$store
訪問到。讓咱們更新下 Counter 的實現:github
// 這是子組件 Counter const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { // 經過this.$store可以訪問到 store 而且獲取到 state return this.$store.state.count } } }
經過這種方式能夠在實際應用中,將 vuex 的初始化分離出去其餘模塊化文件,而後在 vue初始化的引用相關 vuex 的文件便可,而後經過this.$store
全局調用 vuex 的 store 來實現數據存儲。vuex
我整理了一下完整的例子,這是 jsrun的例子:
http://jsrun.net/qWqKpsegmentfault
當一個組件須要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘。爲了解決這個問題,咱們可使用 mapState 輔助函數幫助咱們生成計算屬性,讓你少按幾回鍵。(其實就是自動轉換了一些語法輸出)數組
import { mapState } from 'vuex'
須要先引入纔可使用
mapState使用先後對比:緩存
// 不使用mapState時: computed: { count () { return this.$store.state.count } } // 使用mapState時: computed: mapState({ count: state => state.count, })
若是在大批量的相似這種的計算屬性的話,使用 mapState 會更加便捷,並且不僅是普通字符串,函數也能夠轉換,確實是比較方便的。cookie
這裏會有一個疑問,mapState到底作了什麼事情,怎麼將代碼轉爲 vue 能識別的代碼?因此須要參考 vuex 源代碼中關於mapState的部分:(我找了當前 vuex 版本3.01的源代碼:https://github.com/vuejs/vuex/blob/e821f1bf5b818992d7f34c03029ce2ded1f52a75/dist/vuex.esm.js)app
這是normalizeMap的代碼:
function normalizeMap (map) { // 判斷是否數組,而且最終返回也是一個數組 return Array.isArray(map) // 是數組就直接 map 循環 ? map.map(key => ({ key, val: key })) // 是對象就將 key拿出來,而後再進行 map 循環 : Object.keys(map).map(key => ({ key, val: map[key] })) }
這是mapState的代碼:
var mapState = normalizeNamespace(function (namespace, states) { var res = {}; // 這是一個對象類型 // 將 state 經過normalizeMap格式化變成一個數組,數組元素是一個對象 normalizeMap(states).forEach(function (ref) { var key = ref.key;// 將數組元素對象解出來,先保存起來被後面使用 var val = ref.val; // 組成一個新數組,以 key 爲 key,值是一個函數mappedState res[key] = function mappedState () { var state = this.$store.state; // 將自己 vuex 的 store 的 state 和 getters 保存 var getters = this.$store.getters; // 先不看 namespace 部分 // ...... // 這個函數裏面會判斷真正的值 ref.val 是函數仍是普通值 return typeof val === 'function' ? val.call(this, state, getters) // 函數會被直接執行,而且傳入 vuex 的state 和 getters : state[val] // 值會直接放到 vuex 的state 裏面 }; }); return res });
{}
,一種是數組[]
。由於normalizeMap只作了這2個判斷。{ key, val: key }
這種結構的。countPlusLocalState
,那麼被 object.keys
獲取的 key 是函數的名字。// 例如傳入的state 是一個數組,以下: [ { count: state => state.count, } ] // 那麼被normalizeMap轉換後: // 即轉換爲{ key, val: key }) [ { key, // key 是對象{count: state => state.count} val: key // val是對象{count: state => state.count} }, //..... ] //------------------------ // 例如傳入的state 是一個對象,以下: { count: state => state.count, } // 那麼被normalizeMap轉換後: // 即被Object.keys(map).map(key => ({ key, val: map[key] }))處理後 [ { key, // key 是count,由於被Object.keys提取出來 val: map[key] // val 是state => state.count,這裏以 key 爲鍵找對象的值 }, //..... ]
normalizeMap(states)
格式化以後會使用 forEach 遍歷這個數組:
this.$store.state[val]
做爲 mappedState 的返回值。// 被 foreach 遍歷,繼續用上面的例子的state來舉例,由於不是函數,因此被直接返回: res["count"] = this.$store.state['state => state.count'] // 雖然這裏是以數組的寫法,可是在 js 裏面數組的寫法也能夠用在對象上。 //若是是函數的話,會被執行val.call,而且傳入了幾個參數(this, this.$store.state, this.$store.getters) res["countPlusLocalState"] = this.$store.state['state => state.count']
這是mapState通過源代碼解析前和後的對比:
// states 爲對象時候,mapState轉換前 computed: mapState({ count: state => state.count, // 傳字符串參數 'count' 等同於 `state => state.count` countAlias: 'count', // 爲了可以使用 `this` 獲取局部狀態,必須使用常規函數 countPlusLocalState (state) { return state.count + this.localCount } }) // states 爲對象時候,mapState轉換後 computed: { count() { // 直接轉換爲通常的計算屬性的使用方式 return this.$store.state.count }, countAlias() { // 也是轉爲通常的計算屬性的使用方式,只不過有指定名字的會使用中括號括起來 return this.$store.state['count'] }, countPlusLocalState() { // 由於是函數,因此會被直接執行,而且傳入了當前 store 上的 state 和 getters做爲參數 //(但這裏沒使用 getters) return this.$store.state.count + this.localCount } }
使用 Vuex 並不意味着你須要將全部的狀態放入 Vuex。雖然將全部的狀態放到 Vuex 會使狀態變化更顯式和易調試,但也會使代碼變得冗長和不直觀。若是有些狀態嚴格屬於單個組件,最好仍是做爲組件的局部狀態。你應該根據你的應用開發須要進行權衡和肯定。
Vuex 容許咱們在 store 中定義「getter」(能夠認爲是 store 的計算屬性),就像計算屬性同樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。
// 初始化 getter const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { // 這就是 getters doneTodos: state => { return state.todos.filter(todo => todo.done) } } }) // 使用getter store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }] // 或者能夠this.$store.getters.xxxx 這樣使用。
// 能夠在第二個參數裏面傳一個 getter 做爲參數 getters: { // ... doneTodosCount: (state, getters) => { // 傳入了以前設置的doneTodos的 getters,因此直接使用了doneTodos return getters.doneTodos.length } } store.getters.doneTodosCount // -> 1 // 讓 getter 返回一個函數,來實現給 getter 傳參。在你對 store 裏的數組進行查詢時很是有用。 getters: { // ... getTodoById: (state) => (id) => { // 返回一個函數 return state.todos.find(todo => todo.id === id) } } // 對返回的函數傳入參數來使用 store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性。
參考 vuex 源代碼,相似的處理也是跟...mapState
相似:
export function mapGetters(getters) { const res = {} // 先格式化,而後再處理 normalizeMap(getters).forEach(({key, val}) => { res[key] = function mappedGetter() { if (!(val in this.$store.getters)) { console.error(`[vuex] unknown getter: ${val}`) } return this.$store.getters[val] } }) return res }
相比 mapState 來講,他簡單一點。
惟一的區別就是它的 val 不能是函數,只能是一個字符串,並且會檢查 val in this.$store.getters
的值,若是爲 false 會輸出一條錯誤日誌。
其實三個點就是es6的擴展運算符。
能夠將一個數組轉爲用逗號分隔的參數序列,也能夠將對象進行展開,若是應用到 mapstate 身上就是:
須要注意:vue的 computed 是對象,裏面的屬性是對象屬性。
computed: { // 通常 computed 對象屬性 now: function () { return Date.now() } // 使用對象展開運算符將此對象混入到外部對象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) // 轉換後是這樣,跟通常 computed 對象屬性差很少 doneTodosCount:function(){}, anotherGetter:function(){} }
參考: