請啟用 Javascript 查看內容

Vuex - Module

 ·  ☕ 4 分鐘

竹白的 Vue 記事本 目錄

模組化

如果所有狀態資料都集中在一個比較大的物件,會遇到一個問題:當應用變得非常複雜時,store 物件就有可能變得相當難以維護。

為了解決以上問題,Vuex 允許我們將 store 分割成模組(module)。

分割

每個模組就是一個小型的 store,擁有自己的 statemutationsactionsgetters、甚至是巢狀子模組。

兩種模組結構參考:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// moduleA.js

export default {
  namespaced: true,
  state: () => ({
    a1: 'aaa',
  }),
  mutations: {},
  actions: {},
  getters: {},
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// moduleB.js

const state = () => ({
  b1: 'bbb',
});

const getters = {};
const actions = {};
const mutations = {};

export default {
  state,
  getters,
  actions,
  mutations,
};

為了避免模組重用時,狀態物件通過引用被共享,所以會使用函式型式來宣告模組的狀態。

引入:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// store.js

import Vuex from 'vuex';
import moduleA from './modules/modulesA';
import moduleB from './modules/modulesB';

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {
    moduleA: moduleA,
    moduleB: moduleB,
    
    // 或是使用簡寫
    // moduleA,
    // moduleB,
  },
});

1. 取得模組狀態

模組的的 state 物件將被儲存在 moduleA moduleB 中:

1
2
store.state.moduleA; // { "a1": 'aaa' }
store.state.moduleB; // { "b1": 'bbb' }

CodePen Demo:Vue 2.x -Vuex Module

模組的區域狀態

對於模組內的 mutationgetteraction,透過參數取得的狀態為模組內部的區域狀態。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const moduleA = {
  state: () => ({
    text: '區域'
  }),
  mutations: {
    print(state) {
      console.log(state.text);
    },
  },
};

const store = new Vuex.Store({
  state: {
    text: '全域',
  },
  modules: {
    a: moduleA,
  },
});
1
store.commit('print');   //  '區域'

CodePen Demo:Vue 2.x -Vuex Module 區域狀態

若要取的根 stategetter 函式可以使用第三個參數 rootStateaction 函式也可以透過 context.rootState 取得:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const moduleA = {
  // ...
  getters: {
    foo(state, getters, rootState) {
      // ...
    },
  },
  actions: {
    boo({ rootState }) {
      // ...
    },
  },
}

另外,還有 rootGetters 可以取得根 getters

命名空間

從上面可知,預設情況下模組內的 actionmutationgetter 是註冊在全域空間下。如果全域、區域有相同的名稱的函式都會執行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const moduleA = {
  state: () => ({
    text: '區域'
  }),
  mutations: {
    print(state) {
      console.log(state.text);
    },
  },
};

const store = new Vuex.Store({
  state: {
    text: '全域',
  },
  mutations: {
    print(state) {
      console.log(state.text);
    },
  },
  modules: {
    a: moduleA,
  },
});
1
2
3
store.commit('print');
// '全域'
// '區域'

CodePen Demo:Vue 2.x -Vuex Module 相同名稱

因此,如果你希望模組具有更高度的封裝性,只讓它們在內部有作用,可以設置 namespaced: true 使其成為帶命名空間的模組。

1
2
3
4
const moduleA = {
  namespaced: true,
  // ...
};

啟用命名空間後,如果要呼叫模組內的 mutation 就需要加上路徑:

1
2
3
4
5
6
store.commit('moduleA/print');
// '區域'

// 呼叫相同名稱的全域 mutation 也不會觸發模組內的函式
store.commit('print');
// '全域'

getteraction 也一樣要加上路徑:

1
2
3
4
// getter
store.getters['moduleA/xxxx'];
// action
store.dispatch('moduleA/xxxx');

CodePen Demo:Vue 2.x -Vuex Module 命名空間

1. 訪問全域內容

使用命名空間後,在模組內呼叫的 mutationaction 都會是區域的。但如果我們要在模組內操作全域的 mutationaction 該怎麼做呢?

你可以將 { root: true } 作為 dispatchcommit 方法的第三參數傳入:

1
2
dispatch('someOtherAction', null, { root: true });
commit('someMutation', null, { root: true });

CodePen Demo:Vue 2.x -Vuex Module 訪問全域內容

2. 在命名空間的模組下全域註冊

若你希望在命名空間的模組上,某個函式全域註冊,你可以使用物件的形式定義,並加上 root: true

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const moduleA = {
  namespaced: true,
  actions: {
    foo: {
      root: true,
      handler (context, payload) {
        // ...
      },
    }
  },
};

3. 輔助函式

如果要使用輔助函式引入模組的內容,寫起來會有點繁瑣:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Component.vue

computed: {
  ...mapState({
    a1: (state) => state.moduleA.a1,
    a2: (state) => state.moduleA.a2,
  })
},
methods: {
  ...mapActions({
    foo: 'moduleA/foo',
    boo: 'moduleA/boo',
  }),
}

CodePen Demo:Vue 2.x -Vuex Module 輔助函式

你可以將模組的空間名稱作為第一個參數傳入輔助函式,這樣所有綁定會自動將該模組作為上下文,這樣就能使用陣列型式映射內容,簡化上述繁瑣的程式碼:

1
2
3
4
5
6
7
8
// Component.vue

computed: {
  ... mapState('moduleA', ['a1', 'a2']),
},
methods: {
  ... mapActions('moduleA', ['foo', 'boo']),
}

CodePen Demo:Vue 2.x -Vuex Module 輔助函式 - 模組命名參數

或者透過 createNamespacedHelpers 建立基於模組命名空間的輔助函式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Component.vue
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapActions } = createNamespacedHelpers('moduleA');

export default {
  computed: {
    ... mapState(['a1', 'a2']),
  },
  methods: {
    ... mapActions(['foo', 'boo']),
  }
}

CodePen Demo:Vue 2.x -Vuex Module 輔助函式 - createNamespacedHelpers


竹白
作者
竹白
前端筆記

文章目錄