Vuex的五個核心概念
本文參考自Vue文檔,說的非常詳細屏镊,建議看文檔。
Vuex是什么痰腮?
VueX 是一個專門為 Vue.js 應用設計的狀態(tài)管理架構(gòu)而芥,統(tǒng)一管理和維護各個vue組件的可變化狀態(tài)(你可以理解成 vue 組件里的某些 data )。
Vue有五個核心概念膀值,state,getters,mutations,actions,modules棍丐。本文將對這個五個核心概念進行梳理。
總結(jié)
state => 基本數(shù)據(jù)
getters => 從基本數(shù)據(jù)派生的數(shù)據(jù)
mutations => 提交更改數(shù)據(jù)的方法沧踏,同步歌逢!
actions => 像一個裝飾器,包裹mutations翘狱,使之可以異步秘案。
modules => 模塊化Vuex
State
state即Vuex中的基本數(shù)據(jù)!
單一狀態(tài)樹
Vuex使用單一狀態(tài)樹,即用一個對象就包含了全部的狀態(tài)數(shù)據(jù)阱高。state作為構(gòu)造器選項赚导,定義了所有我們需要的基本狀態(tài)參數(shù)。
在Vue組件中獲得Vuex屬性
我們可以通過Vue的Computed獲得Vuex的state赤惊,如下:
const store =new Vuex.Store({
? ? state: {
? ? ? ? count:0? ? }
})
const app =new Vue({
? ? //..? ? store,
? ? computed: {
? ? ? ? count: function(){
? ? ? ? ? ? returnthis.$store.state.count
? ? ? ? }
? ? },
? ? //..})
每當store.state.count變化的時候, 都會重新求取計算屬性吼旧,并且觸發(fā)更新相關聯(lián)的 DOM。
mapState輔助函數(shù)
當一個組件需要獲取多個狀態(tài)時候未舟,將這些狀態(tài)都聲明為計算屬性會有些重復和冗余圈暗。為了解決這個問題,我們可以使用 mapState 輔助函數(shù)幫助我們生成計算屬性裕膀,讓你少按幾次鍵员串。
// 在單獨構(gòu)建的版本中輔助函數(shù)為 Vuex.mapStateimport { mapState } from 'vuex'export default {
? // ...? computed: mapState({
? ? // 箭頭函數(shù)可使代碼更簡練count: state => state.count,
? ? // 傳字符串參數(shù) 'count' 等同于 `state => state.count`countAlias: 'count',
? ? // 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)? ? countPlusLocalState (state) {
? ? ? returnstate.count +this.localCount
? ? }
? })
}
當映射的計算屬性的名稱與 state 的子節(jié)點名稱相同時魂角,我們也可以給 mapState 傳一個字符串數(shù)組昵济。
computed: mapState([
? // 映射 this.count 為 store.state.count'count'])
對象展開運算符
mapState 函數(shù)返回的是一個對象。我們?nèi)绾螌⑺c局部計算屬性混合使用呢野揪?通常访忿,我們需要使用一個工具函數(shù)將多個對象合并為一個,以使我們可以將最終對象傳給 computed 屬性斯稳。但是自從有了對象展開運算符海铆,我們可以極大地簡化寫法:
computed: {
? localComputed () //本地計算屬性//使用對象展開運算符將此對象混入到外部對象中? ...mapState({
? ? //..? })
}
對象運算符?
...展開運算符(spread operator)允許一個表達式在某處展開。展開運算符在多個參數(shù)(用于函數(shù)調(diào)用)或多個元素(用于數(shù)組字面量)或者多個變量(用于解構(gòu)賦值)的地方可以使用挣惰。
展開運算符不能用在對象當中卧斟,因為目前展開運算符只能在可遍歷對象(iterables)可用。iterables的實現(xiàn)是依靠[Symbol.iterator]函數(shù)憎茂,而目前只有Array,Set,String內(nèi)置[Symbol.iterator]方法珍语,而Object尚未內(nèi)置該方法,因此無法使用展開運算符竖幔。不過ES7草案當中已經(jīng)加入了對象展開運算符特性板乙。
function test(a,b,c) {
? ? ? ? console.log(a);
? ? ? ? console.log(b);
? ? ? ? console.log(c);
? ? }
? ? varargs = [0,1,2];
? ? test(...args);? // 0? 1? 2
ES7草案中的對象展開運算符?
ES6中還不支持對對象的展開運算符,但是ES7中將支持拳氢。對象展開運算符符可以讓我們更快捷地操作對象募逞,如下例子:
let {x,y,...z}={x:1,y:2,a:3,b:4};
? ? x; //1y;//2z;//{a:3,b:4}
組件仍然保有局部狀態(tài)
使用 Vuex 并不意味著你需要將所有的狀態(tài)放入 Vuex。雖然將所有的狀態(tài)放到 Vuex 會使狀態(tài)變化更顯式和易調(diào)試馋评,但也會使代碼變得冗長和不直觀放接。
如果有些狀態(tài)嚴格屬于單個組件,最好還是作為組件的局部狀態(tài)留特。你應該根據(jù)你的應用開發(fā)需要進行權(quán)衡和確定纠脾。
getters
即從store的state中派生出的狀態(tài)玛瘸。
getters接收state作為其第一個參數(shù),接受其他 getters 作為第二個參數(shù)乳乌,如不需要捧韵,第二個參數(shù)可以省略如下例子:
const store =new Vuex.Store({
? ? state: {
? ? ? ? count:0? ? }市咆,
? ? getters: {
? ? ? ? // 單個參數(shù)countDouble:function(state){
? ? ? ? ? ? returnstate.count * 2? ? ? ? },
? ? ? ? // 兩個參數(shù)countDoubleAndDouble:function(state, getters) {
? ? ? ? ? ? returngetters.countDouble * 2? ? ? ? }
? ? }
})
與state一樣汉操,我們也可以通過Vue的Computed獲得Vuex的getters。
const app =new Vue({
? ? //..? ? store,
? ? computed: {
? ? ? ? count: function(){
? ? ? ? ? ? returnthis.$store.state.count
? ? ? ? },
? ? ? ? countDouble: function(){
? ? ? ? ? ? returnthis.$store.getters.countDouble
? ? ? ? },
? ? ? ? countDoubleAndDouble: function(){
? ? ? ? ? ? returnthis.$store.getters.countDoubleAndDouble
? ? ? ? }
? ? },
? ? //..})
mapGetters 輔助函數(shù)
mapGetters 輔助函數(shù)僅僅是將 store 中的 getters 映射到局部計算屬性蒙兰,與state類似
import { mapGetters } from 'vuex'export default {
? // ...? computed: {
? // 使用對象展開運算符將 getters 混入 computed 對象中? ? ...mapGetters([
? ? ? 'countDouble',
? ? ? 'CountDoubleAndDouble',
? ? ? //..? ? ])
? }
}
如果你想將一個 getter 屬性另取一個名字磷瘤,使用對象形式:
mapGetters({
? // 映射 this.double 為 store.getters.countDoubledouble: 'countDouble'})
mutations
提交mutation是更改Vuex中的store中的狀態(tài)的唯一方法。
mutation必須是同步的搜变,如果要異步需要使用action采缚。
每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)。這個回調(diào)函數(shù)就是我們實際進行狀態(tài)更改的地方挠他,并且它會接受 state 作為第一個參數(shù)扳抽,提交載荷作為第二個參數(shù)。(提交荷載在大多數(shù)情況下應該是一個對象),提交荷載也可以省略的殖侵。
const store =new Vuex.Store({
? state: {
? ? count: 1? },
? mutations: {
? ? //無提交荷載? ? increment(state) {
? ? ? ? state.count++? ? }
? ? //提交荷載? ? incrementN(state, obj) {
? ? ? state.count += obj.n
? ? }
? }
})
你不能直接調(diào)用一個 mutation handler贸呢。這個選項更像是事件注冊:“當觸發(fā)一個類型為 increment 的 mutation 時,調(diào)用此函數(shù)拢军±阆荩”要喚醒一個 mutation handler,你需要以相應的 type 調(diào)用 store.commit 方法:
//無提交荷載store.commit('increment')//提交荷載store.commit('incrementN', {
? ? n: 100? ? })
對象風格的提交方式
我們也可以使用這樣包含 type 屬性的對象的提交方式茉唉。
store.commit({
? type: 'incrementN',
? n: 10})
Mutations 需遵守 Vue 的響應規(guī)則
最好提前在你的 store 中初始化好所有所需屬性固蛾。
當需要在對象上添加新屬性時,你應該
使用Vue.set(obj, 'newProp', 123), 或者
以新對象替換老對象度陆。例如艾凯,利用對象展開運算符我們可以這樣寫state.obj = {...state.obj, newProp: 123 }
mapMutations 輔助函數(shù)
與其他輔助函數(shù)類似,你可以在組件中使用 this.$store.commit(‘xxx’) 提交 mutation懂傀,或者使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用(需要在根節(jié)點注入 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鸿竖,而不是直接變更狀態(tài)沧竟。
Action 可以包含任意異步操作。
我們用如下例子來結(jié)束actions:
const store =new Vuex.Store({
? state: {
? ? count: 0? },
? mutations: {
? ? increment (state) {
? ? ? state.count++? ? }
? },
? actions: {
? ? increment (context) {
? ? ? setInterval(function(){
? ? ? ? context.commit('increment')
? ? ? }, 1000)
? ? }
? }
})
注意:Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象缚忧,因此你可以調(diào)用 context.commit 提交一個 mutation悟泵,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
分發(fā)actions
Action 通過store.dispatch方法觸發(fā):
store.dispatch('increment')
其他與mutations類似的地方
Actions 支持同樣的載荷方式和對象方式進行分發(fā):
// 以載荷形式分發(fā)store.dispatch('incrementN', {
? n: 10})// 以對象形式分發(fā)store.dispatch({
? type: 'incrementN',
? n: 10})
mapActions輔助函數(shù)
你在組件中使用this.$store.dispatch('xxx')分發(fā) action闪水,或者使用mapActions輔助函數(shù)將組件的 methods 映射為store.dispatch調(diào)用(需要先在根節(jié)點注入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
使用單一狀態(tài)樹糕非,導致應用的所有狀態(tài)集中到一個很大的對象。但是,當應用變得很大時朽肥,store 對象會變得臃腫不堪禁筏。
為了解決以上問題,Vuex 允許我們將 store 分割到模塊(module)衡招。每個模塊擁有自己的 state篱昔、mutation、action始腾、getters州刽、甚至是嵌套子模塊——從上至下進行類似的分割:
const moduleA = {
? state: { ... },
? mutations: { ... },
? actions: { ... },
? getters: { ... }
}
const moduleB = {
? state: { ... },
? mutations: { ... },
? actions: { ... }
}
const store =new Vuex.Store({
? modules: {
? ? a: moduleA,
? ? b: moduleB
? }
})
store.state.a // -> moduleA 的狀態(tài)store.state.b// -> moduleB 的狀態(tài)
模塊的局部狀態(tài)
對于模塊內(nèi)部的mutation和getter,接收的第一個參數(shù)是模塊的局部狀態(tài),對于模塊內(nèi)部的 getter浪箭,根節(jié)點狀態(tài)會作為第三個參數(shù):
const moduleA = {
? state: { count: 0 },
? mutations: {
? ? increment (state) {
? ? ? // state 模塊的局部狀態(tài)state.count++? ? }
? },
? getters: {
? ? doubleCount (state) {
? ? ? returnstate.count * 2? ? },
? ? sumWithRootCount (state, getters, rootState) {
? ? ? returnstate.count + rootState.count
? ? }
? }
}
同樣穗椅,對于模塊內(nèi)部的 action,context.state是局部狀態(tài)奶栖,根節(jié)點的狀態(tài)是context.rootState:
const moduleA = {
? // ...? actions: {
? ? incrementIfOddOnRootSum (context) {
? ? ? if((context.state.count + context.rootState.count) % 2 === 1) {
? ? ? ? commit('increment')
? ? ? }
? ? }
? }
}