Vuex的全面用法總結


 

1. vuex簡介

vuex是專門用來管理vue.js應用程序中狀態的一個插件。他的作用是將應用中的所有狀態都放在一起,集中式來管理。需要聲明的是,這里所說的狀態指的是vue組件中data里面的屬性。了解vue的同學應該是明白data是怎么回事的吧,如果不懂的話,建議先學完vue的基礎知識再看vuex。

 

 

2. vuex的組成結構示意圖

vuex的特點是把數據單獨隔離,形成一棵樹狀圖。單獨隔離就意味着它有自己的生態系統。輸入和輸出,其中action作為數據的輸入,state作為數據的輸出。如下圖:

vuex里有這么一個規則:

只能在mutaions里修改state,actions不能直接修改state

 

mutations即變化,修改state的數據,而且只能是同步的,不能存在異步的操作。如果需要異步怎么辦呢?把異步操作放在actions里,拿到數據再通過mutations同步處理。vuex做的其實是把職權明確了,責任細分了。所以它文檔里也說,小系統可以不用。狀態數據少,沒有細分的必要。

通過這個圖,我們很容易就對vuex的組成部分,以及vuex與組件(components)之間的聯系一目了然。其實這張圖,有點迷惑人,暫時看不懂也沒關系,只要有一個直觀的認識就行了。這張描述圖就不用了,大家請往下看。

 

3. vuex 的核心概念

 

3.1 store

vuex 中最關鍵的是store對象,這是vuex的核心。可以說,vuex這個插件其實就是一個store對象,每個vue應用僅且僅有一個store對象。

3.1.1 創建store

const store = new Vuex.Store({...});

可見,store是Vuex.Store這個構造函數new出來的實例。在構造函數中可以傳一個對象參數。這個參數中可以包含5個對象:

  • 1.state – 存放狀態

  • 2.getters – state的計算屬性

  • 3.mutations – 更改狀態的邏輯,同步操作

  • 4.actions – 提交mutation,異步操作

  • 5.mudules – 將store模塊化

關於store,需要先記住兩點:

  • 1. store 中存儲的狀態是響應式的,當組件從store中讀取狀態時,如果store中的狀態發生了改變,那么相應的組件也會得到更新;

  • 2. 不能直接改變store中的狀態。改變store中的狀態的唯一途徑是提交(commit)mutations。這樣使得我們可以方便地跟蹤每一個狀態的變化。

3.1.2 一個完整的store的結構是這樣的

const store = new Vuex.Store({
 state: {
   // 存放狀態
 },
 getters: {
   // state的計算屬性
 },
 mutations: {
   // 更改state中狀態的邏輯,同步操作
 },
 actions: {
   // 提交mutation,異步操作
 },
 // 如果將store分成一個個的模塊的話,則需要用到modules。
  //然后在每一個module中寫state, getters, mutations, actions等。
 modules: {
   a: moduleA,
   b: moduleB,
   // ...
 }
});

3.2 state

state上存放的,說的簡單一些就是變量,也就是所謂的狀態。沒有使用 state 的時候,我們都是直接在 data 中進行初始化的,但是有了 state 之后,我們就把 data 上的數據轉移到 state 上去了。另外有些狀態是組件私有的狀態,稱為組件的局部狀態,我們不需要把這部分狀態放在store中去。

 

3.2.1 如何在組件中獲取vuex狀態

由於vuex的狀態是響應式的,所以從store中讀取狀態的的方法是在組件的計算屬性中返回某個狀態。

 

import store from 'store';
const Counter = {
 template: `<div>{{ count }}</div>`,
 computed: {
   count () {
     // 獲取store中的狀態
     return store.state.count;
   }
 }
}

這樣,組件中的狀態就與store中的狀態關聯起來了。每當store.state.count發生變化時,都會重新求取計算屬性,從而更新DOM。

然而,每個組件中都需要反復倒入store。可以將store注入到vue實例對象中去,這樣每一個子組件中都可以直接獲取store中的狀態,而不需要反復的倒入store了。

const app = new Vue({
 el: '#app',
 // 把 store 對象注入到了
 store,
 components: { Counter },
 template: `
       <div>
           <counter></counter>
       </div>
   `
});

這樣可以在子組件中使用this.$store.state.count訪問到state里面的count這個狀態

 

const Counter = {
 template: `<div>{{ count }}</div>`,
 computed: {
   count () {
     // 獲取store中的狀態
     return this.$store.state.count;
   }
 }
}

3.2.2 mapState

當一個組件獲取多種狀態的時候,則在計算屬性中要寫多個函數。為了方便,可以使用mapState輔助函數來幫我們生成計算屬性。

 

import { mapState } from  'vuex';
export default {
 // ...
 data (){
   localState: 1
 }
 computed: mapState({
   // 此處的state即為store里面的state
   count: state => state.count,
   // 當計算屬性的名稱與state的狀態名稱一樣時,可以省寫
   // 映射 this.count1 為 store.state.count1
   count1,
   //'count'等同於 ‘state => state.count’
   countAlias: 'count',
   countPlus (state){
   // 使用普通函數是為了保證this指向組件對象
     return state.count + this.localState;
   }
 })
}
//上面是通過mapState的對象來賦值的,還可以通過mapState的數組來賦值
computed: mapState(['count']);
//這種方式很簡潔,但是組件中的state的名稱就跟store中映射過來的同名

對象擴展運算符

mapState 函數返回的是一個對象,為了將它里面的計算屬性與組件本身的局部計算屬性組合起來,需要用到對象擴展運算符。

computed: {
 localState () {
 ... mapState ({
     
   })
 }

這樣,mapState中的計算屬性就與localState計算屬性混合一起了。

 

3.3 getters

有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數。此時可以用到getters,getters可以看作是store的計算屬性,其參數為state。

 

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

3.3.1 獲取getters里面的狀態,方法一

 

store.getters.doneTodos //  [{ id: 1, text: 'reading', done: true }]
//在組件中,則要寫在計算屬性中,
computed: {
 doneTodos () {
   return this.$store.getters.doneTodos;
 }
}

3.3.2 使用mapGetters獲取getters里面的狀態:方法二

import {mapState, mapGetters} from 'vuex';
computed: {
...mapState(['increment']),
...mapGetters(['doneTodos'])
}

3.4 mutations

mutations里面是如何更改state中狀態的邏輯。更改Vuex中的state的唯一方法是,提交mutation,即store.commit(‘increment’)。

3.4.1 提交載荷(payload)

可以向commit傳入額外的參數,即mutation的載荷。

 

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

payload還可以是一個對象。

 

mutations: {
 increment(state, payload)
 state.count += payload.amount;
}
}
store.commit('increment', {amount: 10});

還可以使用type屬性來提交mutation。

 

store.commit({
 type: 'increment',
 amount: 10
});
// mutations保持不變
mutations: {
 increment(state, payload){
   state.count += payload.amount;
 }
}

注意:mutation必須是同步函數,不能是異步的,這是為了調試的方便。

 

 

3.4.2 在組件中提交mutations

那么mutation應該在哪里提交呢? 因為js是基於事件驅動的,所以改變狀態的邏輯肯定是由事件來驅動的,所以store.commit(‘increment’)是在組件的methods中來執行的。

方法1: 在組件的methods中提交

 

methods: {
 increment(){
   this.$store.commit('increment');
 }
}

 

方法2: 使用mapMutaions

用 mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用。

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

// 因為mutation相當於一個method,所以在組件中,可以這樣來使用
<button @click="increment">+</button>

3.5 actions

因為mutations中只能是同步操作,但是在實際的項目中,會有異步操作,那么actions就是為了異步操作而設置的。這樣,就變成了在action中去提交mutation,然后在組件的methods中去提交action。只是提交actions的時候使用的是dispatch函數,而mutations則是用commit函數。

3.5.1 一個簡單的action

const store = new Vuex.Store({
 state: {
   count: 0
 },
 mutations: {
   increment(state){
     state.count++;
   }
 },
 actions: {
   increment(context){
     context.commit('increment');
   }
   /* 可以用參數結構的方法來寫action
    increment({commit}){
    commit('increment');
    }
    */
 }
});

// action函數接受一個context參數,這個context具有與store實例相同的方法和屬性。

// 分發action
store.dispatch('increment');

action同樣支持payload和對象方式來分發,格式跟commit是一樣的,不再贅述。

 

4.5.2 在組件中分發action

方法1: 在組件的methods中,使用this.$store.dispatch(‘increment’)。

方法2: 使用mapActions,跟mapMutations是類似的。

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

// 同樣在組件中,可以這樣來使用
<button @click="increment">+</button>

 

3.5.3 組合actions

因為action是異步的,那么我們需要知道這個異步函數什么時候結束,以及等到其執行后,會利用某個action的結果。這個可以使用promise來實現。在一個action中返回一個promise,然后使用then()回調函數來處理這個action返回的結果。

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

// 這樣就可以操作actionA返回的結果了
store.dispatch('actionA').then(() => {
 // dosomething ...
});

// 也可以在另一個action中使用actionA的結果
actions: {
 // ...
 actionB({ dispatch, commit }){
   return dispatch('actionA').then(() => {
     commit('someOtherMutation');
   })
 }
}

4 mudules

module是為了將store拆分后的一個個小模塊,這么做的目的是因為當store很大的時候,分成模塊的話,方便管理。

4.1 每個module擁有自己的state, getters, mutation, action

const moduleA = {
   state: {...},
   getters: {...},
   mutations: {....},
 actions: {...}
}

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

const store = new Vuex.Store({
 modules: {
   a: moduleA,
   b: moduleB
 }
});

store.state.a // 獲取moduleA的狀態
store.state.b // 獲取moduleB的狀態

4.2 模塊內部的狀態

對於模塊內部的mutation和getter,接受的第一個參數是模塊的局部狀態state。順便說一下,根結點的狀態為rootState。

 

const moduleA = {
 state: { count: 0},
 getters: {
   doubleCount(state){
     return state.count * 2;
   }
 },
 mutations: {
   increment(state){
     state.count ++ ;
   }
 },
 actions: {...}
}

 

4.3 模塊的動態注冊

在模塊創建之后,可以使用store.registerModule方法來注冊模塊。

 

store.registerModule('myModule', {
 // ...
});

依然的,可以通過store.state.myModule來獲取模塊的狀態。

可以使用store.unregisterModule(moduleName)來動態的卸載模塊,但是這種方法對於靜態模塊是無效的(即在創建store時聲明的模塊)。

5 含有vuex的項目的結構

5.1 應該遵循的規則

  • 1. 應用層級的狀態都應該集中在store中

  • 2. 提交 mutation 是更改狀態state的唯一方式,並且這個過程是同步的。

  • 3. 異步的操作應該都放在action里面


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM