vuex 版本爲
^2.3.1
,按照我本身的理解來整理vuex。vue
每一個vuex 應用只有一個 store 實例,因此使用起來不會太複雜,對於定位錯誤狀態和操做會很方便。git
最基本的使用方式,經過在vue文件裏面初始化 vuex 的 store 來進行操做 vuex 的數據:以下例子:es6
// 在組件裏面使用 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 之類的東西,或者是一些多個模塊之間共享的屬性。github
因此 vuex 提供一個新的方式來將 vuex 的 store 存放到根組件下,經過 store 選項,將store從根組件「注入」到每個子組件中(需調用 Vue.use(Vuex)
):vuex
// 初始化 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 的實現:segmentfault
// 這是子組件 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 來實現數據存儲。數組
我整理了一下完整的例子,這是 jsrun的例子: jsrun.net/qWqKp緩存
當一個組件須要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘。爲了解決這個問題,咱們可使用 mapState 輔助函數幫助咱們生成計算屬性,讓你少按幾回鍵。(其實就是自動轉換了一些語法輸出)cookie
import { mapState } from 'vuex'
須要先引入纔可使用app
mapState使用先後對比:
// 不使用mapState時:
computed: {
count () {
return this.$store.state.count
}
}
// 使用mapState時:
computed: mapState({
count: state => state.count,
})
複製代碼
若是在大批量的相似這種的計算屬性的話,使用 mapState 會更加便捷,並且不僅是普通字符串,函數也能夠轉換,確實是比較方便的。
這裏會有一個疑問,mapState到底作了什麼事情,怎麼將代碼轉爲 vue 能識別的代碼?因此須要參考 vuex 源代碼中關於mapState的部分:(我找了當前 vuex 版本3.01的源代碼:github.com/vuejs/vuex/…)
這是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(){}
}
複製代碼
參考: