學習vue之前,最重要是弄懂兩個概念,一是“what”,要理解vuex是什么;二是“why”,要清楚為什么要用vuex。
Vuex是什么?
Vuex 類似 React 里面的 Redux 的狀態管理器,用來管理Vue的所有組件狀態。
為什么使用Vuex?
當你打算開發大型單頁應用(SPA),會出現多個視圖組件依賴同一個狀態,來自不同視圖的行為需要變更同一個狀態。
遇到以上情況時候,你就應該考慮使用Vuex了,它能把組件的共享狀態抽取出來,當做一個全局單例模式進行管理。這樣不管你在何處改變狀態,都會通知使用該狀態的組件做出相應修改。
下面講解如何使用Vuex
一個簡單的Vuex示例
本文就講解安裝Vuex,直接通過代碼講解Vuex使用。
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
上面就是一個簡單的Vuex示例,每一個Vuex應用就是一個store,在store中包含組件中的共享狀態state和改變狀態的方法(暫且稱作方法)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狀態更新的時候,組件數據得到相應的更新,那么可以用計算屬性computed獲取state的更新狀態。
const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return store.state.count; } } }
每一個store.state都是全局狀態,在使用Vuex時候需要在根組件或(入口文件)注入。
// 根組件 import Vue from 'vue'; import Vuex from '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([ 'count' // 映射 this.count 為 store.state.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 } from 'vuex'; export default { // ... computed: { ...mapGetters([ ... ]), ...mapState([ ... ]) } }
Mutations
之前也說過了,更改Vuex的store中的狀態的唯一方法就是mutations。
每一個mutation都有一個事件類型type和一個回調函數handler。
調用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') }) } }
Actions
注 Mutations必須是同步函數。
如果我們需要異步操作和提交多個Mutations,Mutations就不能滿足我們需求了,這時候我們就需要Actions了。
Actions
Action 類似于 mutation,不同在于:
讓我們來注冊一個簡單的 action:
var store = new Vuex.Store({ state: { count: 0 }, mutations: { increment: function(state) { state.count++; } }, actions: { increment: function(store) { store.commit('increment'); } } });
分發 Action
Action 函數接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調用 context.commit 提交一個 mutation,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
分發 Action
Action 通過 store.dispatch 方法觸發:
乍一眼看上去感覺多此一舉,我們直接分發 mutation 豈不更方便?實際上并非如此,還記得 mutation必須同步執行這個限制么?Action就不受約束! 我們可以在 action 內部執行異步操作:
actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }
Actions 支持同樣的載荷方式和對象方式進行分發:
// 以載荷形式分發 store.dispatch('incrementAsync', { amount: 10 }) // 以對象形式分發 store.dispatch({ type: 'incrementAsync', amount: 10 })
mapActions
同樣地,action也有相對應的mapActions 輔助函數
mapActions
mapActions 輔助函數跟mapMutations一樣都是組件的 methods 調用:
import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ 'increment' // 映射 this.increment() 為 this.$store.dispatch('increment') ]), ...mapActions({ add: 'increment' // 映射 this.add() 為 this.$store.dispatch('increment') }) } }
mutation-types
關于mutation-types方面的講解官方文檔很少說明,但在實際的中大項目中,對==mutation-types==的配置是必不可少的,Vuex的文檔只講解了state,getters,mutation,actions四個核心概念,下面我簡單補充下mutation-types的使用。
顧名思義,==mutation-types==其實就是mutation實例中各個方法的設定,一般要mutation方法前先在mutation-types用大寫寫法設定,再在mutation里引入使用,下面看看項目實際使用:
項目組織結構
在mutation-types定義好mutation的方法結構:
//SET_SINGER,SET_SONG 為mutation中要使用的方法名 export const SET_SINGER = 'SET_SINGER' export const SET_SONG = 'SET_SONG'
在mutation中導入使用:
import * as types from ',/mutation-types.js' const mutations = { [types.SET_SINGER](state, singer) { .... }, [types.SET_SONG](state, song) { .... } }
結語
看完上面對vuex的講解相信你已經入門了,現在可以看看具體的項目加深理解,可以參考我的github一個購物車例子: https://github.com/osjj/vue-shopCart
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com