文章

Vuex 模組化管理

在 Vuex 中,模組化管理可以幫助你將大型應用的狀態管理劃分為多個小的、相對獨立的模塊。這樣,每個模塊可以擁有自己的 statemutationactiongetter,使代碼更加結構化和易於維護。

1. 為什麼使用模組化?

當應用規模變大,數據管理變得越來越複雜時,單一的 Vuex store 可能會變得難以維護。模組化管理的好處包括:

  • 劃分業務邏輯:不同的功能區塊可以劃分成獨立的模塊。
  • 易於維護和擴展:每個模塊都有自己的狀態和邏輯,清晰且易於理解。
  • 避免命名衝突:模塊中的狀態和方法可以與其他模塊隔離。

2. 模組的結構

每個 Vuex 模組可以包含 statemutationsactionsgetters 等與全局 store 相同的屬性。模組可以按照功能或業務邏輯進行劃分。

模塊化 Store 的結構:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
};

這個模塊 moduleA 擁有自己的狀態、變更邏輯、異步操作和 getter 方法。


3. 在主 Store 中註冊模組

要在主 store 中使用模組,首先需要在創建 Vuex store 的時候將模組註冊進去。模組可以通過 modules 屬性來注冊,並且每個模塊都有自己獨立的命名空間。

註冊模組:

1
2
3
4
5
6
7
8
9
10
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    a: moduleA  // 註冊 moduleA
  }
});

這樣,moduleA 已經被註冊為 store 的一部分,我們可以在應用中使用它的狀態、變更方法等。


4. 訪問模塊中的狀態與方法

一旦模組被註冊,可以通過 Vuex store 的 stategettersactionsmutations 等來訪問模組中的數據。

訪問模塊中的狀態和 getter:

1
2
3
4
5
6
7
8
9
<template>
  <div>
    <!-- 訪問 moduleA 中的 count -->
    <p>模組A計數: </p>

    <!-- 訪問 moduleA 中的 getter -->
    <p>雙倍計數: </p>
  </div>
</template>

提交 mutation 和 dispatch action:

1
2
3
4
5
6
7
8
9
<template>
  <div>
    <!-- 提交 moduleA 中的 mutation -->
    <button @click="$store.commit('a/increment')">模組A 增加</button>

    <!-- 分發 moduleA 中的 action -->
    <button @click="$store.dispatch('a/incrementAsync')">模組A 異步增加</button>
  </div>
</template>

注意:當我們在模組中定義的 mutationaction 被註冊時,它們的名稱會自動加上模組的名稱作為前綴,比如:a/increment


5. 命名空間(Namespace)

在 Vuex 中,默認情況下模塊中的狀態和方法是全局的,這可能導致命名衝突。為了解決這個問題,Vuex 提供了命名空間(namespace)。通過將模塊設置為有命名空間的模塊,可以讓其方法更具封閉性和獨立性。

使用命名空間:

可以在模塊中設置 namespaced: true 來啟用命名空間:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const moduleA = {
  namespaced: true,  // 開啟命名空間
  state: () => ({
    count: 0
  }),
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
};

在這種情況下,moduleA 中的 mutationaction 會自動附加上命名空間前綴,並且必須通過完整的命名來調用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <div>
    <!-- 訪問有命名空間的狀態 -->
    <p>模組A計數: </p>

    <!-- 提交有命名空間的 mutation -->
    <button @click="$store.commit('a/increment')">模組A 增加</button>

    <!-- 分發有命名空間的 action -->
    <button @click="$store.dispatch('a/incrementAsync')">模組A 異步增加</button>

    <!-- 訪問有命名空間的 getter -->
    <p>雙倍計數: </p>
  </div>
</template>

這樣可以保證模塊內部的方法不會和其他模塊或全局的 mutationactiongetter 發生命名衝突。


6. 模組之間的通信

模組化之後,有時候模組之間仍然需要相互通信。即使它們有各自的命名空間,也可以通過以下方式來互相調用:

  • 跨模塊提交 mutation分發 action
    • 可以使用 root 屬性來訪問全局的 state、mutation 或 action。

範例:

1
2
3
4
5
6
7
8
9
10
const moduleA = {
  namespaced: true,
  actions: {
    incrementAsync({ commit, rootState }) {
      if (rootState.b.count > 10) {
        commit('increment');
      }
    }
  }
};

在這裡,moduleA 通過 rootState.b.count 訪問模塊 b 中的狀態。


7. 結構化模塊的組織

隨著應用的增大,模塊本身也可能變得複雜。你可以將每個模塊拆分為多個文件並以更具結構化的方式來管理它們。

範例目錄結構:

1
2
3
4
5
store/
  └── modules/
        ├── moduleA.js
        └── moduleB.js
  └── index.js

store/index.js 中引入所有模塊並創建 store:

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from 'vue';
import Vuex from 'vuex';
import moduleA from './modules/moduleA';
import moduleB from './modules/moduleB';

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
});

這樣的結構能夠使你的應用更加乾淨和易於管理,特別是在模塊越來越多的情況下。


8. 總結

  • Vuex 的模塊化管理適合應對大型應用的狀態管理需求,將應用劃分為多個獨立的模塊。
  • 模塊中可以包含自己的 statemutationsactionsgetters,並且可以使用命名空間來避免命名衝突。
  • 可以通過 rootStateroot 屬性在不同模塊之間進行通信。
  • 模組化使得應用狀態管理更加結構化、易於維護和擴展。

透過模組化,Vuex 變得更靈活、可維護性更強,非常適合處理複雜的應用邏輯。

本文章以 CC BY 4.0 授權