Vuex學習筆記

Vuex 是什么?

** 官方解釋:Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式**。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。

vuex其實就是用一個全局的變量保存了Vue項目中的所有的公共數據,類似與在前端這塊放了一個數據庫,大家都可以在這里存數據,刪數據,改數據,讀數據,是不是有點熟悉:增,刪,改,查;不過這個全局的變量給他定義了一個固定的名字就叫:store(倉庫),是不是很形象,而這個倉庫里面裝數據的袋子就是state,加工數據的機器就叫做:mutations,操作機器的工人就叫做:actions,把數據裝起來取走的卡車就叫做:getters;
所以一個簡單的vuex就是:

new vuex.store({
    state,
    mutations,
    actions,
    getters
})

Vuex 特點

  • Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。

  • 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交(commit) mutations。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。

為什么使用Vuex?

當你要開發一個大型的SPA應用的時候,會出現:多個視圖公用一個狀態、不同視圖的行為要改變同一個狀態的情況,遇到這種情況的時候就需要考慮使用Vuex了,它會把組件的共享狀態抽取出來,當做一個全局單例模式進行管理,這樣不管你何時何地改變狀態,都會通知到使用該狀態的組件做出相應的修改;

Vuex實例

import Vue from 'vue';
import Vuex form 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        increment (state) {
            state.count++
        }
    }
})

這就是一個vuex的最簡單實例,store就是組件中的共享狀態,而改變狀態的方法(其實是一個對象包含很多方法,但都是來改變store的)叫做:mutations;
需要特變注意的是只能通過mutations改變store的state的狀態,不能通過store.state.count = 5;直接更改(其實可以更改,不建議這么做,不通過mutations改變state,狀態不會被同步)。
使用store.commit方法觸發mutations改變state:

store.commit('increment');
console.log(store.state.count)  // 1

這樣一個簡簡單單的Vuex應用就實現了。

在Vue組件里使用Vuex

Vuex的狀態獲取是一個方法,當Vuex狀態更新時,相應的Vue組件也要更新,所以Vue應該在計算屬性(computed)獲取state;

// Counter 組件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return store.state.count;
        }
    }
}

上面的例子是直接操作全局狀態store.state.count,那么每個使用該Vuex的組件都要引入。為了解決這個,Vuex通過store選項,提供了一種機制將狀態從根組件注入到每一個子組件中。

// 根組件
import Vue from 'vue';
import Vuex form 'vuex';
Vue.use(Vuex);
const app = new Vue({
    el: '#app',
    store,
    components: {
        Counter
    },
    template: `
        <div class="app">
            <counter></counter>
        </div>
    `
})

通過這種注入機制,就能在子組件Counter通過this.$store訪問:

// Counter 組件
const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count () {
            return this.$store.state.count
        }
    }
}
mapState函數
computed: {
    count () {
        return this.$store.state.count
    }
}

這樣通過count計算屬性獲取同名state.count屬性,是不是顯得太重復了,我們可以使用mapState函數簡化這個過程。

import { mapState } from 'vuex';
export default {
    computed: mapState ({
        count: state => state.count,
        countAlias: 'count',    // 別名 `count` 等價于 state => state.count
    })
}

還有更簡單的使用方法:

computed: mapState([
  // 映射 this.count 為 store.state.count
  'count'
])
Getters對象

如果我們需要對state對象進行做處理計算,如下:

computed: {
    doneTodosCount () {
        return this.$store.state.todos.filter(todo => todo.done).length
    }
}

如果多個組件都要進行這樣的處理,那么就要在多個組件中復制該函數。這樣是很沒有效率的事情,當這個處理過程更改了,還有在多個組件中進行同樣的更改,這就更加不易于維護。

Vuex中getters對象,可以方便我們在store中做集中的處理。Getters接受state作為第一個參數:

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

在Vue中通過store.getters對象調用。

computed: {
  doneTodos () {
    return this.$store.getters.doneTodos
  }
}

Getter也可以接受其他getters作為第二個參數:

getters: {
  doneTodos: state => {
      return state.todos.filter(todo => todo.done)
  },
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
mapGetters輔助函數

與mapState類似,都能達到簡化代碼的效果。mapGetters輔助函數僅僅是將store中的getters映射到局部計算屬性:

import { mapGetters } from 'vuex'
export default {
  // ...
  computed: {
    // 使用對象展開運算符將 getters 混入 computed 對象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

上面也可以寫作:

computed: mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])

所以在Vue的computed計算屬性中會存在兩種輔助函數:

import { mapState, mapGetters } form 'vuex';
export default {
    // ...
    computed: {
        mapState({ ... }),
        mapGetter({ ... })
    }
}
Mutations

更改Vuex的store中的狀態的唯一方法就是mutations.
每一個mutation都有一個事件類型type和一個回調函數handler

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態
      state.count++
    }
  }
})

調用mutation,需要通過store.commit方法調用mutation type:

store.commit('increment')
Payload 提交載荷

也可以向store.commit傳入第二參數,也就是mutation的payload:

mutaion: {
    increment (state, n) {
        state.count += n;
    }
}
store.commit('increment', 10);

單單傳入一個n,可能并不能滿足我們的業務需要,這時候我們可以選擇傳入一個payload對象:

mutation: {
    increment (state, payload) {
        state.totalPrice += payload.price + payload.count;
    }
}
store.commit({
    type: 'increment',
    price: 10,
    count: 8
})
mapMutations函數

不例外,mutations也有映射函數mapMutations,幫助我們簡化代碼,使用mapMutations輔助函數將組件中的methods映射為store.commit調用。

import { mapMutations } from 'vuex'
export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // 映射 this.increment() 為 this.$store.commit('increment')
    ]),
    ...mapMutations({
      add: 'increment' // 映射 this.add() 為 this.$store.commit('increment')
    })
  }
}

注 Mutations必須是同步函數(即沒有異步操作)。
如果我們需要異步操作,Mutations就不能滿足我們需求了,這時候我們就需要Actions了。

Aciton對象

Action 類似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接變更狀態。
  • Action 可以包含任意異步操作(彌補了mutation不能有異步操作的不足)。
    讓我們來注冊一個簡單的 action:
const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {      //此處的context具有與store實例相同的方法和屬性
          context.commit('increment')
        }
      }
})

Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以:

  • 調用 context.commit,提交一個 mutation;
  • 通過 context.statecontext.getters來獲取 stategetters
    當我們在之后介紹到 Modules(https://vuex.vuejs.org/zh-cn/modules.html) 時,你就知道 context 對象為什么不是 store 實例本身了。
    實踐中,我們會經常會用到 ES2015 的 參數解構 來簡化代碼(特別是我們需要調用 commit很多次的時候):
actions: {
    increment ({ commit }) {
      commit('increment')
    }
}
分發 Action

Action 通過 store.dispatch (與commit相對應)方法觸發:

store.dispatch('increment')

乍一眼看上去感覺多此一舉,我們直接分發 mutation 豈不更方便?實際上并非如此,還記得mutation 必須同步執行這個限制么?Action 就不受約束!我們可以在 action內部執行異步操作:

actions: {
    incrementAsync ({ commit }) {
        setTimeout(() => {  //模擬異步請求
            commit('increment')
        }, 1000)
    }
}

Actions 支持同樣的載荷方式和對象方式進行分發:

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

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

來看一個更加實際的購物車示例,涉及到調用異步 API 和 分發多重 mutations:

actions: {
  checkout ({ commit, state }, products) {
    // 把當前購物車的物品備份起來
    const savedCartItems = [...state.cart.added]
    // 發出結賬請求,然后樂觀地清空購物車
    commit(types.CHECKOUT_REQUEST)
    // 購物 API 接受一個成功回調和一個失敗回調
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失敗操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

注意我們正在進行一系列的異步操作,并且通過提交 mutation 來記錄 action 產生的副作用(即狀態變更)。

在組件中分發 Action

action為改變狀態(數據)的方法,所以應該放在Vue組件的methods中;

你在組件中使用 this.$store.dispatch('xxx') 分發action,或者使用 mapActions 輔助函數將組件的 methods映射為 store.dispatch 調用(需要先在根節點注入 store):

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment' // 映射 this.increment() 為 this.$store.dispatch('increment')
    ]),
    ...mapActions({
      add: 'increment' // 映射 this.add() 為 this.$store.dispatch('increment')
    })
  }
}
組合 Actions

Action 通常是異步的,那么如何知道 action 什么時候結束呢?更重要的是,我們如何才能組合多個 action,以處理更加復雜的異步流程?

首先,你需要明白 store.dispatch 可以處理被觸發的action的回調函數返回的Promise,并且store.dispatch仍舊返回Promise

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

現在你可以在Vue組件中:

store.dispatch('actionA').then(() => {
  // ...
})

在另外一個 action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

最后,如果我們利用 async / await 這個 JavaScript 即將到來的新特性,我們可以像這樣組合 action:

// 假設 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

一個 store.dispatch在不同模塊中可以觸發多個 action 函數。在這種情況下,只有當所有觸發函數完成后,返回的 Promise 才會執行。

Modules

Vuex只能有一個單一的store對象,但是當應用變得龐大復雜的時候store就可能變得非常的臃腫;
所以為了解決這個問題Vuex增加了** 模塊(module)**的概念,也就是說你可以在這個store對象里面再建子對象了,而且這些子對象都有: statemutationactiongetter嵌套子模塊(再在子模塊中從上至下進行同樣方式的分割)

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 的狀態
store.state.b // -> moduleB 的狀態
模塊的局部狀態

對于模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象。

const moduleA = {
  state: { count: 0 },//模塊的局部狀態對象
  mutations: {
    increment (state) {
      // 這里的 `state` 對象是模塊的局部狀態
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同樣,對于模塊內部的 action,局部狀態通過 context.state 暴露出來, 根節點狀態則為 context.rootState:

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

對于模塊內部的 getter,根節點狀態會作為第三個參數暴露出來:

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

文章里面代碼直接使用的是文檔里面的,感覺文檔也不是十分的難理解,后面的命名空間,暫時沒有整理,有時間看下后續整理吧,大家有什么問題可以在評論區一起討論,;-)
** 文章部分引用:**
https://yeaseonzhang.github.io/2017/03/16/Vuex-%E9%80%9A%E4%BF%97%E7%89%88/
https://vuex.vuejs.org/zh-cn/modules.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • vuex 狀態管理器 作為應用中所有組件的中央儲存 只能以預定的方式去操作狀態 把所有組件共享的狀態抽取出來作為全...
    一只大椰子閱讀 795評論 0 1
  • 安裝 npm npm install vuex --save 在一個模塊化的打包系統中,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 2,957評論 0 7
  • 上一章總結了 Vuex 的框架原理,這一章我們將從 Vuex 的入口文件開始,分步驟閱讀和解析源碼。由于 Vuex...
    你的肖同學閱讀 1,803評論 3 16
  • Vuex是什么? Vuex 是一個專為 Vue.js應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件...
    蕭玄辭閱讀 3,130評論 0 6
  • VUEX 是一個狀態管理模式,可以集中式存儲和管理組件的狀態。 組件使用store里面的state作數據展示,通過...
    Liberty寒風閱讀 1,228評論 1 1