【前端筆記】Vuex快速使用

樑仔發表於2019-01-21

本文對 Vuex 官方文件重新組織編排,希望正在學習 Vue 的同學們,在閱讀後可快速使用 Vuex。

開始使用 Vuex,把狀態拿到應用外部管理,Vuex管這個管理狀態的玩意叫 Store,一個完全獨立的應用,他只負責狀態管理。嘗試把 Vuex 應用和 Vue 應用劃清界限,

  • 一個 Vuex 應用,做狀態管理,可以理解是 Model 層
  • 一個 Vue 應用,僅負責資料展示,純純的 View 層

Vuex 應用

所謂狀態管理,無非就是定義狀態,修改狀態

定義 state

在 Vuex 裡定義狀態,我們需要 new 一個 Store 出來,每一個 Vuex 應用的核心就是 store(倉庫)。

// store.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0
  }
});

export default store;
複製程式碼

以上程式碼建立了一個 store,store.state 裡定義了狀態。與在 Vue 裡定義 data 沒有任何區別,

下面修改狀態。

直接修改

如果你想快點用上 Vuex,你可以在元件裡直接修改 store 裡的 state,(直接修改的意思就是,用點操作修改)

state.count = 2;
複製程式碼

雖然這樣可以正常工作,但在嚴格模式下會報錯,更改 store 中的狀態的唯一方法應該是提交 mutation

嚴格模式

開啟嚴格模式,僅需在建立 store 的時候傳入 strict: true

const store = new Vuex.Store({
  strict: true
});
複製程式碼

在嚴格模式下,無論何時發生了狀態變更且不是由 mutation 函式引起的,將會丟擲錯誤。

mutation

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。

Vuex.Store的構造器選項中,有一個 mutation 選項,這個選項就像是事件註冊:key 是一個字串表示 mutation 的型別(type),value 是一個回撥函式(handler),

這個回撥函式就是我們實際進行狀態更改的地方,它有兩個入參,第一個引數是 state,就是 store 裡的 state;第二個引數是 Payload,這是提交 mutation 時候額外傳入的引數。

定義 mutation

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

提交 mutation

上面定義了 mutation,要喚醒一個 mutation handler,唯一的介面是store.commit,如果你熟悉事件監聽,commit 就類似於 Vue 的$emit,jquery 的trigger

可想而知,commit 方法傳參必須至少有一個能區分 mutation 的唯一標識,這個標識就是 type

commit 引數是物件

當 commit 的引數是一個物件的時候,物件裡必須要有 type 欄位,除了 type 欄位,你還可以新增額外任意欄位為載荷(payload)。

// 物件風格的提交方式
store.commit({
  type: "increment",
  amount: 10
});
複製程式碼

type 做第一引數

把 type 和 payload 分開也是個不錯的選擇,可以把 type 單獨拿出來當第一個引數,以commit(type,[payload])的形式提交,官方稱此為以載荷形式提交

store.commit("increment");
store.commit("increment", 10);
store.commit("increment", { count: 2 });
複製程式碼

在大多數情況下,payload 應該是一個物件,這樣可以包含多個欄位並且記錄的 mutation 會更易讀。

到這裡我們已經知道如何定義 mutation 和提交 mutation 了,commit 介面很簡單,但在哪裡使用呢?有兩個地方用

  • Vue 應用的元件裡,在元件裡呼叫store.commit
  • 還有 store 的 action 裡。

Action

狀態管理不過就是定義 state 和修改 state,mutation 已經可以修改 state 了,為什麼還需要 action?

同步和非同步

在 Vuex 中,mutation 都是同步事務,在 mutation 中混合非同步呼叫會導致你的程式很難除錯。

例如,當你呼叫了兩個包含非同步回撥的 mutation 來改變狀態,你怎麼知道什麼時候回撥和哪個先回撥呢?這就是為什麼我們要區分這兩個概念。

Action 確實和 mutation 很類似,不同在於:

  • Action 提交的是 mutation,而不是直接變更狀態。
  • Action 可以包含任意非同步操作。

註冊 action:

註冊 action 就跟定義 mutation 一樣,除了 handler 的入參不同。

Action 函式的入參是一個與 store 例項具有相同方法和屬性的 context 物件。因此可以

  • context.commit 提交一個 mutation,
  • context.state獲取 state。
  • context.getters 獲取 getters。
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment(context) {
      context.commit("increment");
    }
  }
});
複製程式碼

分發 Action

Action 通過 store.dispatch方法觸發:

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

// 以物件形式分發
store.dispatch({
  type: "incrementAsync",
  amount: 10
});
複製程式碼

組合 Action

store.dispatch 可以處理被觸發的 action 的處理函式返回的 Promise,並且 store.dispatch 仍舊返回 Promise,因此,通過  async / await,很方便控制流程

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

表單處理

表單的問題在於使用v-model時候,v-model會試圖直接修改 state,而 Vuex 在嚴格模式下是不允許直接修改 state 的。

很容易解決,只要我們把修改 state 的行為按 Vuex 的要求以commit mutation 方式修改即可。

有兩種方式。

用“Vuex 的思維”解決

拋棄v-model指令,自己去實現v-model雙向繫結,非常簡單,

  • input標籤的value屬性繫結對應從 store 中對映來的計算屬性,
  • 監聽 input 事件,用 commit mutation 的方式,修改 store 裡的 state。
<input :value="message" @input="updateMessage" />
複製程式碼
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},

methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

複製程式碼

store 中的 mutation 函式:

mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}
複製程式碼

用 Vue 計算屬性解決

如果堅持使用v-model,可以在 Vue 裡對v-model繫結的計算屬性設定 set 行為。

<input v-model="message" />
複製程式碼
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
複製程式碼

使用 Vuex 進行狀態管理,到此結束。

從 flux 架構來看,三個核心,StateMutationAction,已經足夠了,總結一下其實很簡單,同步 commit mutation,非同步 dispatch action

核心原則

  • 應用層級的狀態應該集中到單個 store 物件中。
  • 提交 mutation 是更改狀態的唯一方法,並且這個過程是同步的。
  • 非同步邏輯都應該封裝到 action 裡面。

記住這些原則,開始動手把 Vuex 整合到 Vue 專案中吧,至於一些更多的概念,都是些錦上添花的東西。

Vue 應用使用 Vuex

Vuex 的常用 api 上面都涉及到了,使用思路就是

  • 定義狀態:通過new Vuex.Store({})建立一個 store 例項,定義 state,getters,actions,mutations。
  • 改變狀態:使用store.commit提交 mutation,使用store.dispatch分發actions

現在狀態管理的部分已經全部由 store 例項去管理了,如何在 Vue 元件中使用 store,非常簡單,一點也不神奇。

store.js檔案裡我們建立了 store 例項並將其匯出了(export),按照 js 模組化的知識,我們只要在需要的地方匯入它就可以直接使用了。

元件引入 store

可以在需要的元件裡直接引入,就像引入一個普通的 js 一樣。

import store from "path/to/store.js";
複製程式碼

元件中引入了 store,就可以直接通過store.state引用 state,以及直接使用 store 例項簡潔的 api,真的就是這麼簡單。

store.state.count = 2; // 直接修改,並不建議
store.commit(); // 在 store 中呼叫 mutation
store.dispatch("increment"); // 在 store 中分發 action
複製程式碼

我們往往需要在 Vue 元件的template中展示狀態,由於 Vuex 的狀態儲存是響應式的,從 store 例項中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態:

import store from "path/to/store.js";
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count() {
      return store.state.count;
    }
  }
};
複製程式碼

每當 store.state.count 變化的時候, 都會重新求取計算屬性,並且觸發更新相關聯的 DOM。

其實到現在為止,對於在 Vue 中使用 Vuex 就已經足夠了。下面的東西其實可以不用看了,但 Vuex 還是提供了一些方便我們使用的方式,錦上添花。

元件中引入的方式,缺點:這種模式導致元件依賴全域性狀態單例。在模組化的構建系統中,在每個需要使用 state 的元件中需要頻繁地匯入。

全域性引入 store

Vuex 通過 store 選項,提供了一種機制將狀態從根元件“注入”到每一個子元件中:

// app.js
import Vue from "vue";

import store from "path/to/store.js";

const app = new Vue({
  store // 把 store 物件提供給 “store” 選項,這可以把 store 的例項注入所有的子元件
});
複製程式碼

通過在根例項中註冊 store 選項,該 store 例項會注入到根元件下的所有子元件中,且子元件能通過 this.$store 訪問到。這樣我們就不用每個元件單獨引入了。

this.$store.state.count = 2; // 直接修改,並不建議
this.$store.commit("", {}); // 直接在元件中提交 mutation
this.$store.dispatch("increment"); // 在元件中分發 action
複製程式碼

元件繫結的輔助函式

  • mapState
  • mapGetters
  • mapMutations
  • mapActions

既然是輔助,就不是必須的東西,輔助函式可以方便的把 Vuex 中的 state,getter,mutation,action 對映到元件,方便呼叫。

更多概念

Getter

如果你很喜歡 Vue 提供的計算屬性(computed),Vuex 允許在 store 中定義“getter”(可以認為是 store 的計算屬性)。

但對狀態管理來說,Getter 其實並不是必須的,如果你需要的話,可以檢視文件使用。

Module

模組化並不是狀態管理的概念,也不是必須的,但如果應用十分複雜,將 store 分割成模組(module)會是一個必經之路,Vuex 提供了模組化選項,需要可檢視文件。

原文連結:歡迎github來顆star github.com/liangzai92/…

【前端筆記】Vuex快速使用

相關文章