Vuex的五個核心概念
本文參考自Vue文檔,說的非常詳細,建議看文檔。
Vuex是什麼?
VueX 是一個專門爲 Vue.js 應用設計的狀態管理架構,統一管理和維護各個vue組件的可變化狀態(你可以理解成 vue 組件裏的某些 data )。
Vue有五個核心概念,state
, getters
, mutations
, actions
, modules
。本文將對這個五個核心概念進行梳理。
總結
state => 基本數據
getters => 從基本數據派生的數據
mutations => 提交更改數據的方法,同步!
actions => 像一個裝飾器,包裹mutations,使之可以異步。
modules => 模塊化Vuex
State
state即Vuex中的基本數據!
單一狀態樹
Vuex使用單一狀態樹,即用一個對象就包含了全部的狀態數據。state
作爲構造器選項,定義了所有我們需要的基本狀態參數。
在Vue組件中獲得Vuex屬性
- 我們可以通過Vue的
Computed
獲得Vuex的state,如下:
const store = new Vuex.Store({
state: {
count:0
}
})
const app = new Vue({
//..
store,
computed: {
count: function(){
return this.$store.state.count
}
},
//..
})
每當 store.state.count
變化的時候, 都會重新求取計算屬性,並且觸發更新相關聯的 DOM。
mapState輔助函數
當一個組件需要獲取多個狀態時候,將這些狀態都聲明爲計算屬性會有些重複和冗餘。爲了解決這個問題,我們可以使用 mapState 輔助函數幫助我們生成計算屬性,讓你少按幾次鍵。
// 在單獨構建的版本中輔助函數爲 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭頭函數可使代碼更簡練
count: state => state.count,
// 傳字符串參數 'count' 等同於 `state => state.count`
countAlias: 'count',
// 爲了能夠使用 `this` 獲取局部狀態,必須使用常規函數
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
當映射的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字符串數組。
computed: mapState([
// 映射 this.count 爲 store.state.count
'count'
])
對象展開運算符
mapState 函數返回的是一個對象。我們如何將它與局部計算屬性混合使用呢?通常,我們需要使用一個工具函數將多個對象合併爲一個,以使我們可以將最終對象傳給 computed 屬性。但是自從有了對象展開運算符,我們可以極大地簡化寫法:
computed: {
localComputed () //本地計算屬性
//使用對象展開運算符將此對象混入到外部對象中
...mapState({
//..
})
}
-
對象運算符
...
展開運算符(spread operator)允許一個表達式在某處展開。展開運算符在多個參數(用於函數調用)或多個元素(用於數組字面量)或者多個變量(用於解構賦值)的地方可以使用。展開運算符不能用在對象當中,因爲目前展開運算符只能在可遍歷對象(iterables)可用。iterables的實現是依靠[Symbol.iterator]函數,而目前只有Array,Set,String內置[Symbol.iterator]方法,而Object尚未內置該方法,因此無法使用展開運算符。不過ES7草案當中已經加入了對象展開運算符特性。
例子:
function test(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
}
var args = [0,1,2];
test(...args); // 0 1 2
- ES7草案中的對象展開運算符
ES6中還不支持對對象的展開運算符,但是ES7中將支持。對象展開運算符符可以讓我們更快捷地操作對象,如下例子:
let {x,y,...z}={x:1,y:2,a:3,b:4};
x; //1
y; //2
z; //{a:3,b:4}
組件仍然保有局部狀態
使用 Vuex 並不意味着你需要將所有的狀態放入 Vuex。雖然將所有的狀態放到 Vuex 會使狀態變化更顯式和易調試,但也會使代碼變得冗長和不直觀。
如果有些狀態嚴格屬於單個組件,最好還是作爲組件的局部狀態。你應該根據你的應用開發需要進行權衡和確定。
getters
即從store的state
中派生出的狀態。
getters接收state作爲其第一個參數,接受其他 getters 作爲第二個參數,如不需要,第二個參數可以省略如下例子:
const store = new Vuex.Store({
state: {
count:0
},
getters: {
// 單個參數
countDouble: function(state){
return state.count * 2
},
// 兩個參數
countDoubleAndDouble: function(state, getters) {
return getters.countDouble * 2
}
}
})
與state一樣,我們也可以通過Vue的Computed
獲得Vuex的getters。
const app = new Vue({
//..
store,
computed: {
count: function(){
return this.$store.state.count
},
countDouble: function(){
return this.$store.getters.countDouble
},
countDoubleAndDouble: function(){
return this.$store.getters.countDoubleAndDouble
}
},
//..
})
mapGetters 輔助函數
mapGetters 輔助函數僅僅是將 store 中的 getters 映射到局部計算屬性,與state類似
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用對象展開運算符將 getters 混入 computed 對象中
...mapGetters([
'countDouble',
'CountDoubleAndDouble',
//..
])
}
}
如果你想將一個 getter 屬性另取一個名字,使用對象形式:
mapGetters({
// 映射 this.double 爲 store.getters.countDouble
double: 'countDouble'
})
mutations
提交mutation是更改Vuex中的store中的狀態的唯一方法。
mutation必須是同步的,如果要異步需要使用action。
每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。這個回調函數就是我們實際進行狀態更改的地方,並且它會接受 state 作爲第一個參數,提交載荷作爲第二個參數。(提交荷載在大多數情況下應該是一個對象),提交荷載也可以省略的。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
//無提交荷載
increment(state) {
state.count++
}
//提交荷載
incrementN(state, obj) {
state.count += obj.n
}
}
})
你不能直接調用一個 mutation handler。這個選項更像是事件註冊:“當觸發一個類型爲 increment 的 mutation 時,調用此函數。”要喚醒一個 mutation handler,你需要以相應的 type 調用 store.commit 方法:
//無提交荷載
store.commit('increment')
//提交荷載
store.commit('incrementN', {
n: 100
})
對象風格的提交方式
我們也可以使用這樣包含 type 屬性的對象的提交方式。
store.commit({
type: 'incrementN',
n: 10
})
Mutations 需遵守 Vue 的響應規則
- 最好提前在你的 store 中初始化好所有所需屬性。
- 當需要在對象上添加新屬性時,你應該
- 使用
Vue.set(obj, 'newProp', 123)
, 或者 - 以新對象替換老對象。例如,利用對象展開運算符我們可以這樣寫
state.obj = {...state.obj, newProp: 123 }
- 使用
mapMutations 輔助函數
與其他輔助函數類似,你可以在組件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 輔助函數將組件中的 methods 映射爲 store.commit 調用(需要在根節點注入 store)。
import { mapMutations } from 'vuex'
export default {
//..
methods: {
...mapMutations([
'increment' // 映射 this.increment() 爲 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 爲 this.$store.commit('increment')
})
}
}
actions
Action 類似於 mutation,不同在於:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意異步操作。
我們用如下例子來結束actions:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
setInterval(function(){
context.commit('increment')
}, 1000)
}
}
})
注意:Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
分發actions
Action 通過 store.dispatch
方法觸發:
store.dispatch('increment')
其他與mutations類似的地方
Actions 支持同樣的載荷方式和對象方式進行分發:
// 以載荷形式分發
store.dispatch('incrementN', {
n: 10
})
// 以對象形式分發
store.dispatch({
type: 'incrementN',
n: 10
})
mapActions輔助函數
你在組件中使用 this.$store.dispatch('xxx')
分發 action,或者使用 mapActions
輔助函數將組件的 methods 映射爲 store.dispatch
調用(需要先在根節點注入 store
):
import { mapActions } from 'vuex'
export default {
//..
methods: {
...mapActions([
'incrementN' //映射 this.incrementN() 爲 this.$store.dispatch('incrementN')
]),
...mapActions({
add: 'incrementN' //映射 this.add() 爲 this.$store.dispatch('incrementN')
})
}
}
Modules
使用單一狀態樹,導致應用的所有狀態集中到一個很大的對象。但是,當應用變得很大時,store 對象會變得臃腫不堪。
爲了解決以上問題,Vuex 允許我們將 store 分割到模塊(module)。每個模塊擁有自己的 state、mutation、action、getters、甚至是嵌套子模塊——從上至下進行類似的分割:
模塊的局部狀態
對於模塊內部的 mutation
和 getter
,接收的第一個參數是模塊的局部狀態,對於模塊內部的 getter,根節點狀態會作爲第三個參數:
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// state 模塊的局部狀態
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
},
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
同樣,對於模塊內部的 action,context.state
是局部狀態,根節點的狀態是 context.rootState
:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum (context) {
if ((context.state.count + context.rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
推薦看文章 1 https://baijiahao.baidu.com/s?id=1618794879569468435&wfr=spider&for=pc