Vuex 初步學習

一扭粱、概念

每一個 Vuex 應用的核心就是 store(倉庫)慌盯,它包含著你的應用中大部分的狀態(tài) (state)瓢省。Vuex 和單純的全局對象有以下兩點不同。

  • Vuex 的狀態(tài)存儲是響應式的。若 store 中的狀態(tài)發(fā)生變化憋沿,那么相應的組件也會相應地得到高效更新。
  • 不能直接改變 store 中的狀態(tài)。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation锻狗。

簡單的設置一個vue的store:

// 如果在模塊化構建系統(tǒng)中,請確保在開頭調用了 Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

store.commit('increment')   //使用改變屬性的方法

console.log(store.state.count)  // -> 1  直接獲取屬性的值

二焕参、State

屬性存放位置

Vuex 使用單一狀態(tài)樹轻纪,作為一個“唯一數(shù)據(jù)源而存在。由于 Vuex 的狀態(tài)存儲是響應式的龟糕,從 store 實例中讀取狀態(tài)最簡單的方法就是在計算屬性中返回某個狀態(tài)桐磁。

// 創(chuàng)建一個 Counter 組件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {   //計算屬性
    count () {
      return store.state.count
    }
  }
}
//每當 store.state.count 變化的時候, 都會重新求取計算屬性,并且觸發(fā)更新相關聯(lián)的 DOM讲岁。

這種模式導致組件依賴全局狀態(tài)單例我擂,在模塊化的構建系統(tǒng)中,在每個需要使用 state 的組件中需要頻繁地導入缓艳,并且在測試組件時需要模擬狀態(tài)校摩。

mapState 輔助函數(shù)

當一個組件需要獲取多個狀態(tài)時候,將這些狀態(tài)都聲明為計算屬性會有些重復和冗余阶淘。此時可以使用 mapState 輔助函數(shù)幫助我們生成計算屬性衙吩。

  • 寫法一:對象形式
// 在單獨構建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'

export default {
    computed: mapState({
        count: state => state.count,
        
        countAlias: 'count',   // 傳字符串參數(shù) 'count' 等同于 `state => state.count`
        
        countPlusLocalState (state) {   // 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)
            return state.count + this.localCount
        }
    })
}
  • 寫法二:數(shù)組形式(當映射的計算屬性的名稱與 state 的子節(jié)點名稱相同時)
computed: mapState(['count'])   
對象展開運算符

如何將它與局部計算屬性混合使用溪窒?需要使用一個工具函數(shù)將多個對象合并為一個坤塞,以使我們可以將最終對象傳給 computed 屬性。

因此有了對象展開運算符澈蚌,我們可以極大地簡化原有的寫法:

import {mapState} from 'vuex'
 
computed: {
  localComputed () { /* ... */ },   //局部計算屬性
  
  // 使用對象展開運算符將此對象混入到外部對象中
  ...mapState({
        count: state => state.countModel.count,  //可以有多個模塊文件
  })
  
  //數(shù)組形式
  ...mapState(['count'])
}
組件仍然保有局部狀態(tài)

雖然將所有的狀態(tài)放到 Vuex 會使狀態(tài)變化更顯式和易調試摹芙,但也會使代碼變得冗長和不直觀。如果有些狀態(tài)嚴格屬于單個組件宛瞄,最好還是作為組件的局部狀態(tài)浮禾。你應該根據(jù)你的應用開發(fā)需要進行權衡和確定。

二份汗、Getter

獲取屬性值

Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)盈电。getter 的返回值會根據(jù)它的依賴被緩存起來,且只有當它的依賴值發(fā)生了改變才會被重新計算杯活。
Getter

通過屬性訪問

  • Getter 會暴露為 store.getters 對象匆帚,你可以以屬性的形式訪問這些值:
store.getters.count
  • 接受 state 作為其第一個參數(shù),也可以接受其他 getter 作為第二個參數(shù)
getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length  //getters是獲取的對象
  }
}

通過方法訪問

  • 可以通過讓 getter 返回一個函數(shù)旁钧,來實現(xiàn)給 getter 傳參卷扮。
state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
},
  
getters: {
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

//使用
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

mapGetters 輔助函數(shù)

將 store 中的 getter 映射到局部計算屬性:

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用對象展開運算符將 getter 混入 computed 對象中
    ...mapGetters([
      'doneTodosCount',
      'getTodoById',
      doneCount: 'doneTodosCount'  //重新取一個名字
      // ...
    ])
  }
}

三荡澎、Mutation

更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。

vuex中mutation都類似于事件:每一個mutation都有一個字符串的類型type和一個回調函數(shù)晤锹,回調函數(shù)即為我們實際修改屬性值狀態(tài)更改的地方摩幔,接受 state 作為第一個參數(shù):

export default{
    mutations: {
        increment (state) {
          state.count++;   // 變更狀態(tài)
        }
    }
}

不能直接調用一個 mutation的回調函數(shù),該選項更像事件注冊鞭铆,觸發(fā)increment類型的mutation時或衡,會直接調用改類型下的回調函數(shù),需要相應的type調用store.commit:

store.commit('increment')

提交載荷(Payload)

可以向 store.commit 傳入額外的參數(shù)车遂,即 mutation 的 載荷(payload)

mutations: {
  increment (state, n) {
    state.count += n
  }
}

//使用
store.commit('increment', 10)

大多數(shù)情況下封断,載荷應該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀

store.commit('increment', {
  amount: 10
})

對象風格的提交方式

提交 mutation 可以直接使用包含 type 屬性的對象:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
//對象風格的提交方式
store.commit({
  type: 'increment',
  amount: 10
})

Mutation 需遵守 Vue 的響應規(guī)則

因為Vuex 的 store 中的狀態(tài)是響應式的舶担,因此變更狀態(tài)時坡疼,監(jiān)視狀態(tài)的 Vue 組件也會自動更新

  • 最好提前在你的 store 中初始化好所有所需屬性。
  • 當需要在對象上添加新屬性時衣陶,你應該
    • 使用 Vue.set(obj, 'newProp', 123), 或者
    • 以新對象替換老對象
    state.obj = { ...state.obj, newProp: 123 }  //添加一個對象
    
    以上寫法
      let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
      x; // 1
      y; // 2
      z; // { a: 3, b: 4 }
    

使用常量替代 Mutation 事件類型

可以使 linter 之類的工具發(fā)揮作用柄瑰,同時把這些常量放在單獨的文件中可以對整個 app 包含的 mutation 一目了然,(可以把常量都放在一個單獨的文件中剪况,也可以寫在當前文件中)

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數(shù)名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

在組件中提交 Mutation

  • 在組件中使用以下代碼提交
this.$store.commit('類型')
  • mapMutations 輔助函數(shù)
import { mapMutations } from 'vuex'

export default {
  methods: {
    ...mapMutations([    // `mapMutations` 也支持載荷:
      'increment', 
      'incrementBy'
    ]),
    ...mapMutations({    //重新取名字
      add: 'increment'
    })
  }
}

//使用
this.increment()教沾;  //映射為 this.$store.commit('increment')
this.incrementBy(amount);  //映射為this.$store.commit('incrementBy', amount)
this.add();   //映射為this.$store.commit('increment')

Mutation 必須是同步函數(shù)

==堅決不能是異步函數(shù)== , mutation 都是同步事務

在 mutation 中混合異步調用會導致你的程序很難調試译断。當你調用了兩個包含異步回調的 mutation 來改變狀態(tài)授翻,你怎么知道什么時候回調和哪個先回調呢?

四孙咪、Action

Action 類似于 mutation堪唐,區(qū)別:

  • Action 提交的是 mutation,而不是直接變更狀態(tài)翎蹈。
  • Action 可以包含任意異步操作羔杨。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象,可以調用 context.commit 提交一個 mutation,context.state 和 context.getters 來獲取 state 和 getters杨蛋,可以用ES2015 的參數(shù)解構increment ({commit,state,getters})

分發(fā) Action

Action 通過 store.dispatch 方法觸發(fā):

store.dispatch('increment')

可以在 action 內部執(zhí)行異步操作,支持同樣的載荷方式和對象方式進行分發(fā):

// 以載荷形式分發(fā)
store.dispatch('increment', {
  amount: 10
})

// 以對象形式分發(fā)
store.dispatch({
  type: 'increment',
  amount: 10
})

store.dispatch('increment',params)

在組件中分發(fā) Action

  • 直接使用dispatch分發(fā)
    this.$store.dispatch('xxx')
    
  • 使用mapActions 輔助函數(shù)
    import { mapActions } from 'vuex'
    export default {
        methods: {
          ...mapActions(['increment', 'incrementBy' ]),
          ...mapActions({
            add: 'increment' 
          })
        }
        mounted() {
            this.increment();   // 映射為this.$store.dispatch('increment')
            this.incrementBy(amount);   //映射為this.$store.dispatch('incrementBy', amount)
            this.add();   //映射為this.$store.dispatch('increment')
        }
    }
    

    組合 Action

    store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise
    actions: {
        actionA ({ commit }) {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              commit('someMutation')
              resolve()
            }, 1000)
          })
        },
        actionB ({ dispatch, commit }) {
          return dispatch('actionA').then(() => {
            commit('someOtherMutation')
          })
        }
    }
    
  • 可以利用 async / await
    // 假設 getData() 和 getOtherData() 返回的是 Promise
    
      actions: {
        async actionA ({ commit }) {
          commit('gotData', await getData())
        },
        async actionB ({ dispatch, commit }) {
          await dispatch('actionA') // 等待 actionA 完成
          commit('gotOtherData', await getOtherData())
        }
      }
    

Module

當應用變得非常復雜時理澎,store就會有很多逞力,此時Vuex 允許我們將 store 分割成模塊。每個模塊擁有自己的 state糠爬、mutation寇荧、action、getter执隧、甚至是嵌套子模塊——從上至下進行同樣方式的分割:

// moduleA.js
export default moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

//store.js
import Vue from 'vue'
import Vuex from 'vuex'
import moduleA from './moduleA'

Vue.use(Vuex)

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

export default  new Vuex.Store({
  modules: {
    moduleA,
    b: moduleB
  }
})

  • 對于模塊內部的 mutation 和 getter揩抡,接收的第一個參數(shù)是模塊的局部狀態(tài)對象户侥。
  • 對于模塊內部的 action,局部狀態(tài)通過 context.state 暴露出來;根節(jié)點狀態(tài)則為 context.rootState
  • 對于模塊內部的 getter,根節(jié)點狀態(tài)rootState會作為第三個參數(shù)暴露出

命名空間

[復雜]

帶命名空間的綁定函數(shù)
computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
  • 你可以通過使用 createNamespacedHelpers 創(chuàng)建基于某個命名空間輔助函數(shù)峦嗤,返回一個對象蕊唐,對象里有新的綁定在給定命名空間值上的組件綁定輔助函數(shù):

      import { createNamespacedHelpers } from 'vuex'
      const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
      export default {
        computed: {
          ...mapState({
            a: state => state.a,
            b: state => state.b
          })
        }
      }
    

后面會更新具體在項目中的使用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烁设,隨后出現(xiàn)的幾起案子替梨,更是在濱河造成了極大的恐慌,老刑警劉巖装黑,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件副瀑,死亡現(xiàn)場離奇詭異,居然都是意外死亡恋谭,警方通過查閱死者的電腦和手機糠睡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疚颊,“玉大人狈孔,你說我怎么就攤上這事〈。” “怎么了除抛?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長母截。 經常有香客問我到忽,道長,這世上最難降的妖魔是什么清寇? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任喘漏,我火速辦了婚禮,結果婚禮上华烟,老公的妹妹穿的比我還像新娘翩迈。我一直安慰自己,他們只是感情好盔夜,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布负饲。 她就那樣靜靜地躺著,像睡著了一般喂链。 火紅的嫁衣襯著肌膚如雪返十。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天椭微,我揣著相機與錄音洞坑,去河邊找鬼。 笑死蝇率,一個胖子當著我的面吹牛迟杂,可吹牛的內容都是我干的刽沾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼排拷,長吁一口氣:“原來是場噩夢啊……” “哼侧漓!你這毒婦竟也來了?” 一聲冷哼從身側響起攻泼,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤火架,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忙菠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體何鸡,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年牛欢,在試婚紗的時候發(fā)現(xiàn)自己被綠了骡男。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡傍睹,死狀恐怖隔盛,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情拾稳,我是刑警寧澤吮炕,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站访得,受9級特大地震影響龙亲,放射性物質發(fā)生泄漏。R本人自食惡果不足惜悍抑,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一鳄炉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搜骡,春花似錦拂盯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至摸吠,卻和暖如春空凸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜕便。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贩幻,地道東北人轿腺。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓两嘴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親族壳。 傳聞我的和親對象是個殘疾皇子憔辫,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容

  • ### store 1. Vue 組件中獲得 Vuex 狀態(tài) ```js //方式一 全局引入單例類 // 創(chuàng)建一...
    蕓豆_6a86閱讀 732評論 0 3
  • 安裝 npm npm install vuex --save 在一個模塊化的打包系統(tǒng)中,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 2,945評論 0 7
  • ### store 1. Vue 組件中獲得 Vuex 狀態(tài) ```js //方式一 全局引入單例類 // 創(chuàng)建一...
    蕓豆_6a86閱讀 346評論 0 0
  • State 單一狀態(tài)樹 Vuex使用單一狀態(tài)樹——用一個對象就包含了全部的應用層級狀態(tài)仿荆。至此它便作為一個“唯一數(shù)據(jù)...
    oWSQo閱讀 1,093評論 0 0
  • 上一章總結了 Vuex 的框架原理贰您,這一章我們將從 Vuex 的入口文件開始,分步驟閱讀和解析源碼拢操。由于 Vuex...
    你的肖同學閱讀 1,790評論 3 16