19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

盛開的太陽發表於2021-05-07

vuex官網: https://vuex.vuejs.org/zh/

一. 前言

不管是Vue,還是 React,都需要管理狀態(state),比如元件之間都有共享狀態的需要。
什麼是共享狀態? 比如一個元件需要使用另一個元件的狀態,或者一個元件需要改變另一個元件的狀態,都是共享狀態。

如果不對狀態進行有效的管理,狀態在什麼時候,由於什麼原因,如何變化就會不受控制,就很難跟蹤和測試了。

在軟體開發裡,有些通用的思想,比如隔離變化,約定優於配置等,隔離變化就是說做好抽象,把一些容易變化的地方找到共性,隔離出來,不要去影響其他的程式碼。約定優於配置就是很多東西我們不一定要寫一大堆的配置,比如我們幾個人約定,view 資料夾裡只能放檢視,不能放過濾器,過濾器必須放到 filter 資料夾裡,那這就是一種約定,約定好之後,我們就不用寫一大堆配置檔案了,我們要找所有的檢視,直接從 view 資料夾裡找就行。

根據這些思想,對於狀態管理的解決思路就是:把元件之間需要共享的狀態抽取出來,遵循特定的約定,統一來管理,讓狀態的變化可以預測。

二.什麼是Vuex

Vuex是一個專門為Vue.js應用程式開發做狀態管理的.

他採用集中式儲存管理應用的所有元件的狀態, 並以響應的規則保證狀態以一種可預測的方式發生變化.

1. 什麼是狀態管理呢?

vuex官網說到的"狀態管理模式", "集中式儲存管理", 這些詞和"狀態管理"都是一個含義. 就是管理狀態.

我們通常會有很多元件, 元件之間可能會共享狀態. 那麼如何定義這個狀態呢? 定義在某一個元件裡面肯定是不合適的, 要定義在最外層.

用vue生態圈來說, 有多個元件要共享狀態, 通常狀態我們用變數來表示, 也就是多個元件之間共享變數. 當共享變數變多, 我們就是用一個物件來儲存, 這個物件就是儲存共享狀態的物件. 通常, 這個物件放在vue頂層的例項中. 其他各個元件都可以使用.

而vue是響應式程式設計方式, 一個元件修改了狀態, 其他元件能夠實時響應麼?這就是Vuex實現的功能.他的主要功能:

  1. 管理狀態: 因為是將各種狀態儲存在一個地方, 所以也叫集中式儲存管理 或者 集中式裝填管理
  2. 響應式: 一個元件修改了狀態, 其他元件能夠實時響應

2. 通常什麼狀態需要放在Vuex中管理呢?

不是所有的狀態都要交給vuex來管理的, 只有在多介面之間共享的狀態, 我們才將其交給vuex來管理. 比如:

  • 使用者登入狀態: 使用者名稱, 頭像, 暱稱等等. 很多頁面可能都會用到使用者的基本資訊, 像這些統一的資訊, 我們就可以放在統一的地方進行管理了.
  • token: 使用者登入的令牌, 某些介面必須有令牌才能訪問, 那麼這些幾口就需要共享token
  • 商品收藏, 購物車中的物品等. 我們在各個介面都可以新增商品蒐藏, 都可以加購, 這時候, 就可以將其放入到vuex裡面

放在vuex中, 不僅能夠共享狀態, 還能夠實時響應.

3. Vuex的設計思想

Vuex 全域性維護著一個物件,使用到了單例設計模式。在這個全域性物件中,所有屬性都是響應式的,任意屬性進行了改變,都會造成使用到該屬性的元件進行更新。並且只能通過 commit 的方式改變狀態,實現了單向資料流模式。

Vuex整合到了Vue的官方除錯工具devtools extension, 提供了諸如零配置time-travel除錯,狀態快照匯入匯出等高階除錯功能.

三. Vuex是如何在多元件間進行狀態管理的?

2.1. 單介面狀態管理

之前我們遇到的都是在單介面進行狀態管理. 單介面的狀態管理有3個部分, 如下圖所示:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

第一部分: state
第二部分: view
第三部分: action

三部分是如何工作的呢? 通常狀態我們會用一個變數來表示, 定義在元件的data屬性中.

<script>
    export default {
        name: "calculate",
      data() {
          return {
            counter: 0
          };
      }
    }
</script>

然後,在頁面中通過語法糖直接引用counter變數. counter的值就在頁面中顯示了.
最後, 我們還可以在頁面中增加action, 比如點選事件, 來改變state的狀態.

以上是在單頁面中狀態管理的流程.

2.2. 多介面狀態管理

舉個例子, 比如,我們有一個calculate.vue元件


      data() {
          return {
            counter: 0
          };
      },
      methods: {
          add() {
            this.counter ++
          },
          dev() {
            this.counter --
          }
      }
    }
</script>

然後在App.vue中引入calculate.vue元件

<script>
import Calculate from './views/calculate'

export default {
  name: 'App',
  components: {
    Calculate,
  }
}
</script>

這時, 如果想要在App.vue中使用calculate.vue中定義的變數counter, 可以麼?
直接使用肯定會報錯, 但Calculate和App兩個元件的關係是父子元件, 可以使用父子元件變數傳遞的方式實現.

如果不是父子關係呢? 需要如何實現呢?我們可以使用vuex.

2.3. Vuex的使用

使用vuex, 首先需要安裝vuex元件

npm install vuex --save
--save表示的含義是: 執行時需要

元件安裝好了, 下面就來看看怎麼用吧.

第一步: 新增vuex程式碼資料夾

在src目錄下新建/src/store/index.js. 通常, vuex都放在store資料夾裡面. 然後在store下面建立一個檔案index.js

第二步: 在index.js檔案中定義vuex元件.

vuex是一個外掛, vue-router也是一個外掛, 外掛的使用方式都是類似的.

1: 引入vue和vuex

import Vue from 'vue';
import Vuex from 'vuex';

2: 安裝vuex

 Vue.use(Vuex);

3: 建立vuex物件

const store = new Vuex.Store({
    state: {
    },
    mutations: {
    },
    actions: {
    },
    getters: {
    },
    modules: {
    }
})

在store中定義了5個物件, 這5個物件是固定的. 每個物件的含義是什麼呢? 後面在詳細說.

4: 匯出vuex

export default store;

5: 在main.js中引入vuex

import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store/index';

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  router,
  render: h => h(App)
})

接下來在來看看2.2中多介面狀態管理的問題. calculate元件中有一個counter, 在父元件App.vue中想要使用counter, 這時候, 這個counter就是一個公共的狀態了, 可以將其定義在vuex中.

2.4 Vuex實現多介面狀態管理

下面來看看使用vuex的方式如何實現呢?

1. 在vuex的state中定義一個變數counter

// 第三步: 建立vuex物件
const store = new Vuex.Store({
  state: {
    counter:0
  }
})

2. 在calculate.vue中使用$store.state.counter來獲取vuex中counter變數

<template>
  <div>
    <h2>{{$store.state.counter}}</h2>
  </div>
</template>

這樣就顯示出來了counter變數了. 並且, 可以在calculate.vue或者App.vue, 或者任何其他元件中都可以直接使用. 如下圖所示:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

接下來要實現+和-的操作.
這個怎麼實現呢? 我們可能會這麼想

<template>
  <div>
    <h2>-------calculate.vue-------</h2>
    <h2>{{$store.state.counter}}</h2>
    <button v-on:click="$store.state.counter++">+</button>
    <button v-on:click="$store.state.counter--">-</button>
  </div>
</template>

$store.state.counter++或者$store.state.counter--不就可以了麼?
雖然, 這樣也能達到效果, 但是Vuex官網推薦我們不要這樣使用, 原因是, 這樣操作完, 我們不能跟蹤到狀態的變化. 這是什麼意思呢, 這就要來看看vuex的設計思想了.

2.5 Vuex式的設計思想

Vuex實質是單例模式的設計思想

  • 將共享的狀態抽取出來, 交給大管家, 進行統一管理
  • 之後, 每一個檢視, 按照規定好的規則, 執行訪問或修改等操作.
  • 這就是vuex背後的思想.

這裡規定好的規則很重要. 規定好的規則是什麼規則呢? 我們來看一下vuex官方給出的一個圖

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

這裡面一共有5個元素

  • Vue compontents
  • State
  • Mutations
  • Action
  • Devtools

    這幾個部分都是做什麼用的呢?

1. Vue Components

在這個圖裡面綠色的部分是Vue compontents(Vue元件), Vue元件可以引用state變數, 還可以觸發操作修改變數的值.

2. State:

State用來儲存的是變數, 變數值可以直接渲染到Vue元件上, 但是約定好的, Vue元件不可直接修改State的值

3. Mutations 和 Devtools

如果Vue元件想要修改state中的狀態, 他不能直接修改State, 而是需要執行commit, 提交到Mutations, 由Mutations觸發修改state的狀態. 為什麼要這樣呢? 這就和Devtools有關係了.

我們看到有一塊灰色的Devtools, 這是什麼呢? 這是Vue開發的一款瀏覽器外掛. 這個外掛可以幫助我們記錄每次state中變數修改的狀態, 為什麼要記錄state的狀態呢?

比如, 我們有多個元件同時修改vuex中的一個狀態, 那麼大家都來改, 最終這個值是誰改的呢? 如果沒有記錄狀態變化, 那麼我們就不知道是誰改的了. Vue提供的Devtools工具, 就可以用來記錄每次修改的狀態.

但是, 如果我們直接修改元件, 那就沒有經過Devtools的流程, Devtools也就記錄不了了. 也就是直接從state修改變數值(紅色箭頭), 而不是走藍色箭頭的流程, 那麼沒有經過Devtools, Devtools也不能記錄上修改的狀態了.

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

4. Action

Action主要是用來進行非同步處理操作的. mutations是用來處理同步操作的, 所以, vue元件想要修改變數的時候, 直接走mutations就可以了, 這樣也可以通過Devtools來記錄修改情況. 但是, 如果非同步怎麼辦呢? devtools只能用來記錄同步狀態, 如果出現非同步, 他就記錄不了了. 所以, 這裡多了一個action. action就是用來處理非同步操作的. 當action處理完了以後, 再交給Mutations來處理, 這時候就是同步的操作了, Devtools也可以處理了.

什麼情況會進行非同步操作呢?
傳送網路請求. 所以在Action模組指向了BackEnd, 向後端傳送網路請求.

2.6 Devtools的使用

1. 安裝Devtools

下面我們就來安裝Devtools
開啟google瀏覽器, 點選右上角三個點-->更多工具-->擴充套件程式-->開啟Chrome網上應用店-->搜尋Devtools, 如下圖:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

找到VueJs的Devtools-->新增至Chrome, 如下圖:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

然後安裝外掛即可.

2. 使用Devtools跟蹤狀態

Chrome安裝好Devtools以後,開啟控制檯, 在選單欄最後多了一個vue, 點開可以看到如下介面:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

其中,第二個按鈕是監控變數狀態變化的.

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

我們如果直接使用$store.state.counter++, 在Devtools是無法監控狀態變化的,但是如果我們使用mutations就可以監控到狀態變化.

3.在mutation中修改counter的值

首先, 我們是要在頁面實現+和-的邏輯, 如下圖:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

這個邏輯在calculate.vue元件會用到, 在App.vue元件也會用到, 因此我們將其定義在vuex外掛中. 在外掛裡面定義兩個方法:increase()和decrease(), 修改變數counter的值

const store = new Vuex.Store({
  state: {
    counter: 100
  },
  mutations: {
    increase(state) {
      state.counter ++;
    },
    decrease(state) {
      state.counter --;
    }
  }
})

這兩個方法increase()和decrease(), 他們自帶的引數就是state.

在呼叫方如何定義呢? 也就是calculate.vue和App.vue中應該如何使用Vuex中定義的兩個mutations方法呢?

<template>
  <div>
    <h2>-------calculate.vue-------</h2>
    <h2>{{$store.state.counter}}</h2>
    <button v-on:click="add()">+</button>
    <button v-on:click="sub()">-</button>
  </div>
</template>

<script>
    export default {
        name: "calculate",
      data() {
          return {

          };
      },
      methods: {
        add() {
          this.$store.commit("increase");
        },
        sub() {
          this.$store.commit("decrease")
        }
      }
    }
</script>

在呼叫方, 我們要使用this.$store.commit()的方式來提交變更.

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

在左側點選+或-,在右側devtools都會記錄每一次狀態變化.

2.7 總結

Vuex 應用的核心就是 store(倉庫), “store”基本上就是一個容器,它包含著你的應用中大部分的狀態 (state)。Vuex 和單純的全域性物件有以下兩點不同:

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

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

四. Vuex的核心概念

Vuex的核心概念一共有5個

  • State
  • Getter
  • Mutations
  • Action
  • Modules

這幾個概念: 其中State, Mutations, Action上面都有提高過. 下面重點來看看Getter和Modules. 在看Getter之前先來看一個概念: 單一狀態樹

4.1 單一狀態樹

什麼是單一狀態樹呢?

比如我們的個人資訊,社保資訊存在社保系統裡, 公積金資訊存在公積金系統裡, 醫保資訊存在醫保系統裡. 這有有好處也有壞處, 好處是資訊更安全,彼此隔離; 壞處是要是辦某一件事,想要所有的資訊, 就要跑很多地方取.

而單一狀態樹的含義就是將所有的資訊都存在一個store中, 如果需要什麼資料, 直接去那一個store中取就好了. 不要在系統中定義多個store, 這樣不方便管理和維護.

單一狀態樹的含義: 在一個專案只建一個store.

4.2 Getter的使用

Getter有些類似於compute計算屬性. 什麼時候使用計算屬性呢? 當我們需要將一個屬性的值經過計算以後顯示出來, 這時候我們通常使用計算屬性.

Getter也是如此: 當一個state屬性需要計算以後顯示出來, 我們就可以使用Getter屬性.
比如現在要計算counter的平方.

如果不使用計算屬性,我們怎麼做呢? 在calculate.vue元件裡我們是這麼寫的.

<h2>{{$store.state.counter * $store.state.counter}}</h2>

1. 使用計算屬性計算平方

然後, 如果在App.vue中也要這麼用, 就再來一段. 觀察: 這程式碼很長, 不利於維護, 我們可以將其放到Getter中. 統一計算以後返回

const store = new Vuex.Store({
  state: {
    counter: 100
  },
  mutations: {
    increase(state) {
      state.counter ++;
    },
    decrease(state) {
      state.counter --;
    }
  },
  getters: {
    // getter中第一個引數是state
    powerCounter(state) {
      return state.counter * state.counter
    }
  }
})

這裡定義了一個計算平方的方法powerCounter(), 他的第一個引數是state, 所以, 我們可以直接拿到counter屬性進行操作. 接下來在呼叫方如何呼叫呢?

 <h2>{{$store.getters.powerCounter}}</h2>

通過$store.getters.powerCounter獲取計算屬性.

2. 在getters中定義方法的第二個引數

如果我們在另一個方法裡想要使用其他getters計算方法怎麼辦呢? 在getters中定義的方法還有預設的第二個引數getters
例: 計算counter的平方 + 100
這時候我們可以怎麼做呢? 如下定義了powerAndAdd方法, 其第二個引數預設是getters.

getters: {
    // getter中第一個引數是state
    powerCounter(state) {
      return state.counter * state.counter
    },

    powerAndAdd(state, getters) {
      return getters.powerCounter + 100
    }
  }

我們在powerCounter已經做了平方的操作了, 接下來我們可以直接使用這個的結果來計算.
在powerAndAdd方法中, 第一個引數依然是state, 第二個引數是getters. 我們可以通過getters獲取到第一個方法powerCounter, 然後在第一個方法的基礎上+100.

在calculate.vue元件中呼叫powerAndAdd方法

<template>
  <div>
    <h2>-------calculate.vue-------</h2>
    <h2>{{$store.state.counter}}</h2>
    <button v-on:click="add()">+</button>
    <button v-on:click="sub()">-</button>
    <h2>counter取平方: {{$store.getters.powerCounter}}</h2>
    <h2>平方後+100: {{$store.getters.powerAndAdd}}</h2>
  </div>
</template>

最終效果如下:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

3. 在getters中自定義帶引數的方法

在getter中, 前面說了, getters中定義的方法, 第一個引數是state, 第二個引數是getters. 這兩個都是預設的. 如果我有一個方法想要傳自定義引數怎麼辦呢?
不能直接在getters後面新增, 可以使用匿名函式接收自定義引數. 比如下面的add方法

getters: {
   // getter中第一個引數是state
   powerCounter(state) {
     return state.counter * state.counter
   },

   powerAndAdd(state, getters) {
     return getters.powerCounter + 100
   },

   add(state, getters) {
     return (num1, num2) => num1 + 100 + num2;
   }
 }

在使用的時候, 直接傳遞兩個引數就可以了,方法如下:

<h2>累加+100: {{$store.getters.add(200, 500)}}</h2>

4.3 Mutations的使用

1. Vuex中store狀態修改的唯一方式: 提交Mutations

  1. Mutations 主要包含兩部分
  • 一部分是事件型別(type)
  • 另一部分是回撥函式(handler), 回撥函式的第一個引數是state

例如:

mutations: {
    increase(state) {
      state.counter ++;
    },
    decrease(state) {
      state.counter --;
    }
  }

我們可以理解為increase是事件型別type, 方法體是回撥函式. 回撥函式的第一個引數是state

mutations方法定義好以後, 如果我們想要呼叫, 使用commit提交的方式呼叫

add() {
  this.$store.commit("increase");
},

2.Mutations傳遞引數

在之前計算頁面有一個+和-, 如果需要+5, +10, -100這時候怎麼處理呢? 我們需要定義一個方法, 接收引數 在Mutation中如何定義引數, 又如何傳遞引數呢?

在Mutation中有兩種資料提交的方式

1. 第一種資料傳遞的方式

我們來看看步驟:
第一步: 在calculate.vue元件定義兩個按鈕, 一個+5, 一個加 +10

    <button v-on:click="addCount(5)">+5</button>
    <button v-on:click="addCount(10)">+10</button>

在定義一個方法addCount()

     methods: {
            add() {
              this.$store.commit("increase");
            },
            sub() {
              this.$store.commit("decrease")
            },
            addCount(num) {

            }
    }

第二步: 在store中定義一個mutation方法, 並且接收一個引數, 如下increaseCount

mutations: {
    increase(state) {
      state.counter ++;
    },
    decrease(state) {
      state.counter --;
    },
    increaseCount(state, num) {
      state.counter += num;
    }
}

increaseCount()方法第一個引數是state, 我們可以用第二個引數來接收變數. 這和getter是不一樣的, getter需要寫一個匿名函式來接收自定義變數
第三步: 在定義好的calculate元件中呼叫store的increaseCount方法

        addCount(num) {
          this.$store.commit("increaseCount", num)
        }

在傳遞引數的時候, 我們將自定義引數放在第二個變數位置.

第四步: 檢視效果

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

1. 第二種資料傳遞的方式

第一種資料傳遞的方式是具體的引數, 第二種資料傳遞的方式傳遞的是物件. 我們來看看第二種

下面來對比比較:
之前使用的是直接傳遞引數的方式

        addCount(num) {
          this.$store.commit("increaseCount", num)
        },

使用物件傳遞引數怎麼寫呢?

addCount(num) {
          this.$store.commit({
            type: "increaseCount",
            num: num
          })
        },

需要注意的是, 寫法不同, 含義有略有區別.

  • 方式一傳遞到mutation中的是具體的引數值.
  • 方式二傳遞到mutation中的是一個物件.

同樣是在mutation中定義方法, 並接受一個引數obj

 increaseCount(state, obj) {
      console.log(obj);
  },

第一種方式傳遞過來的是:
19.Vuex詳細使用說明-一篇文章涵蓋所有知識點
可以看到傳遞過來是具體引數的內容

第二種方式傳遞過來的是一個物件

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點 觀察右側控制檯, 傳輸傳遞過來的是一個物件, 並且是所有引數的物件. 所以, 如果增加num數字, 需要獲取obj.num
    increaseCount(state, obj) {
      console.log(obj);
      state.counter += obj.num;
    },

3. Mutation的響應規則

Vuex的store的state是響應式的, 當state中的資料發生改變時, Vue元件會自動更新.
但是, 需要準守對應的規則

1) 增加新屬性

比如: 我們修改info的name引數.先來看效果

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

第一步: 在store/index.js的state中定義變數info, 並定義修改info的方法updateInfo

const store = new Vuex.Store({
  state: {
    info: {
      name: "王五",
      age: 58,
      sex: "男"
    }
  },
  mutations: {
    updateInfo(state, name) {
      // 修改info中name的值
      state.info["name"] = name;
    }
  }
})

第二步: 在calculate.vue中展示info內容, 並修改info的內容

<template>
  <div>
    <h2>-------calculate.vue-------</h2>
    <h2>info: {{$store.state.info}}</h2>
    <button v-on:click="updateInfo">修改name</button>
  </div>
</template>

<script>
    export default {
      name: "calculate",
      methods: {
        updateInfo() {
          this.$store.commit("updateInfo", "趙六")
        }
      }
    }
</script>

這裡直接呼叫的是store中的mutation方法updateInfo(). 在updateInfo()方法中, 使用state.info["name"]=name的方式重置了name的值,並且在頁面立刻響應式的看到了效果

但是, 不是在任何情況使用state.info["name"]=name賦值都是響應式的, 我們來給info增加一個hobby試一試
第一步: 在store/index.js中增加mutation方法, 新增愛好hobby, hobby屬性之前在info中是沒有的

    updateInfoHobby(state, hobby) {
      state.info["hobby"] = hobby
      console.log(state.info)
    }

第二步: 定義方法修改hobby

        updateInfoHobby() {
          this.$store.commit("updateInfoHobby", "籃球")
        }

第三步: 看效果

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

我們發現, 點選按鈕新增hobby以後, 頁面並沒有響應式的新增hobby, 但是在右側控制檯看到$store.info中確實已經有hobby屬性.

這就是我們要說的Mutation修改state屬性的第一個條件:
要想實現響應式展示, 需要提前在store中初始化好屬性. 如果有些屬性是動態新增的, 提前不知道怎麼辦呢? 我們需要換一種方式新增

updateInfoHobby(state, hobby) {
      Vue.set(state.info, "hobby", hobby);
    }

來看看效果:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

還有一種方式: 就是使用完整的新物件給就物件賦值.

總結:

  1. 提前在store中初始化好需要的屬性
  2. 當給state中的物件新增新屬性的時候,使用下面的方式
             方式一:使用Vue.set(obj, newProp, value)

             方式二: 用新物件給就物件賦值

2) 刪除屬性

當我們需要刪除屬性的時候, 也是使用Vue.delete(obj, prop)可以做到響應式展示

4. Mutation的型別常量

在mutation中, 我們定義了很多事件型別(也就是方法名), 當我們的專案變大時, vuex管理 狀態越來越多, 需要更新狀態的情況越來越多, 那麼意味著Mutation中的方法越來越多. 方法多了,名稱就容易出錯, 所以我們將Mutation中的常量提取出來. 放在一個公共檔案中定義,下面來看看如何實現:
以修改counter方法為例. 我們來將自增方法increase提取出來.

第一步: 新增一個mutation-types.js 檔案, 定義一個常量INCREASW

export const INCREASW = "increase"

第二步. 在store的mutation中使用常量定義方法名

// 引入mutation
import { INCREASW} from './mutation-types'

const store = new Vuex.Store({
 mutations: {
    [INCREASW](state) {
      state.counter ++;
    }
 }
})

這裡使用[]來定義方法名, 作為變數傳入.

第三步: 在calculate.vue中引入mutation-types並且使用commit提交到mutation

 import {INCREASW} from '../store/mutation-types'
        add() {
          this.$store.commit(INCREASW);
        },

這樣就提取了變數. 當需要修改變數名的時候, 我們不用每個地方都修改, 只需要修改mutation-types中的變數名的值.

5 Mutation同步函式

通常情況下, Vuex要求我們Mutation中的方法必須是同步方法. 為什麼呢?
主要的原因是, 當我們使用devtools工具時, devtools工具可以很好的幫我們捕捉mutation的快照. 但如果是非同步操作, 那麼devtools將不能很好地追蹤到這個操作是什麼時候完成的.

舉個例子:
我們將[修改name]這個動作進行非同步處理. 放在setTimeout中,

   updateInfo(state, name) {
      setTimeout(function(){
        state.info["name"] = name;
      }, 1000)
    }

然後點選[修改name]按鈕, 會發現將王五的名字改為趙六, 頁面改了, 但是在devtools工具中沒有改, 如下圖:
19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

這個問題就是上面說的, 在Mutation中儘量不要執行非同步操作, 要是執行非同步操作, devtools可能跟蹤不上.
如果確實有非同步操作, 那麼就使用action. action的功能類似於Mutation, 但是它主要是處理非同步操作的. 下面就來看看Action的使用

4.4 Action的使用

上面已經說過了, action的用法和Mutation的用法類似. 但action主要是處理非同步操作. 如何將寫在mutation中的updateInfo方法中非同步操作替換到action中實現呢?

在mutation中定義的方法

    updateInfo(state, name) {
      setTimeout(function() {
        state.info["name"] = name;
      }, 1000)
    }

在action中定義的方法

actions:{
    aUpdateInfo(context) {
      setTimeout(function () {
        context.commit("updateInfo")
      }, 1000)
    }
  },
mutations: {
    updateInfo(state, name) {
      state.info["name"] = name;
    }
}

我們定義了一個和updateInfo對應的方法 aUpdate. 入參是context, 注意這裡不是state了, 而是整個store. 非同步操作定義在aUpdate方法中, 然後呼叫Mutation中的方法.
注意: 這是官方要求的, action不要自己去修改state, 所以修改state的操作都在Mutation中進行.

接下來, 在按鈕[修改name]的時候, 重新定義click事件, 這次就不能直接指向mutation了, 而是要指向action.
* 呼叫mutation的方法使用: this.$store.commit(...)
* 呼叫action的方法使用: this.$store.dispatch(...)

    updateInfo() {
      // 呼叫mutation
      // this.$store.commit("updateInfo", "趙六")
      // 呼叫action
      this.$store.dispatch("aUpdateInfo", "趙六")
    },

效果如下圖所示:

19.Vuex詳細使用說明-一篇文章涵蓋所有知識點

可以看到這會devtools識別了info資訊的變化

4.5 Module的使用

Module的含義是模組, 我們為什麼要在Vue中引入模組呢?

  1. Vue使用的是單一狀態樹, 那麼也就是說很多狀態會交給vuex來管理
  2. 當應用變得複雜是, store也會變得很臃腫,
    為了解決這個問題, Vuex允許我們將store分割成模組. 每個模組都擁有自己的states, mutations, actions, getters 等等

寫法如下:

const store = new Vuex.Store({
  modules:{
    a:{
      state: {

      },
      getters:{

      },
      mutations:{

      },
      actions: {

      }
    },
    b: {

    }
  }
})

如上, 在store中定義了一個modules, 裡面有兩個模組a和b, a模組中定義了一套getters, states, mutations, actions, b模組也可如此定義

那麼定義好以後如何使用呢? 下面一個一個來看

1. state呼叫

在store/index.js檔案中定義一個moudules, 然後定義state

const module1 = {
  state: {
    message: "這是定義在module1中的state"
  },
}

const store = new Vuex.Store({
  modules:{
    module1: module1
  }
}

如上展示, 如何呼叫module1中的message呢?

    <h2> message : {{$store.state.module1.message}} </h2>

2. getter 呼叫

在module1中增加getters

const module1 = {
  state: {
    message: "這是定義在module2中的state"
  },
  getters:{
    extraMessage: function(state){
      return state.message + "aaaa";
    }
  }
}

前面介紹過getters中定義的屬性, 就相當於computed計算屬性.
接下來看看如何呼叫getters中的計算屬性呢?

<h2> extraMessage: {{$store.getters.extraMessage}}</h2>

在呼叫的時候, 和state有些不同. 這裡不需要指定modules模組名. 首先回去store中定義的getters查詢, 抄不到再去modules1模組中查詢,所以, 我們在定義的時候,儘量不要重名

3. mutation的呼叫

我們定義一個按鈕來更新 message的值

const module1 = {
  state: {
    message: "這是定義在module2中的state"
  },
  getters:{
    extraMessage: function(state){
      return state.message + "aaaa";
    }
  },
  mutations:{
    changeMessage: function(state) {
      state.message = "替換成新的message"
    }
  },
  actions: {

  }
}

接下來看呼叫方, 定義一個按鈕, 替換message的值. 然後changeMessage

<button v-on:click="changeMessage">替換message</button>
        changeMessage() {
          this.$store.commit("changeMessage")
        }

我們看到在呼叫mutation中的方法的時候, 直接使用的是 commit. 和呼叫store中mutation是一樣的.
這裡還可以傳遞引數, 方式方法也和在store中一樣

4. action的呼叫

我們在修改message資訊的地方將其設定為非同步修改, 寫法如下:

const module1 = {
  state: {
    message: "這是定義在module2中的state"
  },
  getters:{
    extraMessage: function(state){
      return state.message + "aaaa";
    }
  },
  mutations:{
    changeMessage: function(state) {
      state.message = "替換成新的message" + ", bbbb";
    }
  },
  actions: {
    aChangeMessage: function(context) {
      setTimeout(() => {
        context.commit("changeMessage")
      }, 1000)
    }
  }
}

在actions中增加了一個setTimeout, 這就是非同步的. 呼叫方在呼叫的時候, 需要使用dispatch指向actions的方法

        changeMessage() {
          this.$store.dispatch("aChangeMessage")
        }

以上就是在modules中定義states, getters, mutations, actions的方法的使用

至此Vuex的用法就全部完事了.

相關文章