一. Module說明
1. 什么是Module?
由於使用單一狀態樹,應用的所有狀態會集中到一個比較大的對象,當應用變得非常復雜時,store 對象就有可能變得相當臃腫;
為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module);
每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊;
2. Module的命名空間
(1). 默認情況下,模塊內部的action和mutation仍然是注冊在全局的命名空間中的:
這樣使得多個模塊能夠對同一個 action 或 mutation 作出響應;
getters 同樣也默認注冊在全局命名空間;
注:上述默認情況下,顯然是不合理的,我們想達到的目的是各個模塊單獨調用自己的模塊類的對象。
(2). 如果我們希望模塊具有更高的封裝度和復用性,可以添加 namespaced: true (注意是 namespaced)的方式使其成為帶命名空間的模塊:當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名;
3. 快速入門-基於$store/store用法
(1). 准備三個vuex文件,分別是index.js、user1.js、user2.js,其中user1.js 和 user2.js是子模塊,需要在index.js中進行導入。
user1.js

const user1Module = { namespaced: true, //所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名 state() { return { userCounter1: 1000 } }, getters: { // 訪問: $store.getters['user1/getInfo'],這里的user1是指被導入的時候,modules中的命名 // 四個參數如下 getInfo(state, getters, rootState, rootGetters) { return `userCounter1:${state.userCounter1}`; } }, mutations: { // 調用:$store.commit('user1/increase') // 第一個參數是模塊的局部狀態對象state increase(state) { state.userCounter1++; }, }, actions: { // 調用$store.dispatch('user1/increaseAction') // 6個參數如下 increaseAction({ commit, dispatch, state, rootState, getters, rootGetters }) { setTimeout(() => { commit('increase') }, 1000); }, // 子module中調用根module中的方法 fIncrease({ commit, dispatch, state, rootState, getters, rootGetters }) { commit('increase', null, { root: true }); // 或 // dispatch('increaseAction', null, { root: true }); } } } export default user1Module
user2.js

const user2Module = { namespaced: true, state() { return { userCounter2: 2000 } }, getters: { // 訪問: $store.getters['user2/getInfo'],這里的user2是指被導入的時候,modules中的命名 // 四個參數如下 getInfo(state, getters, rootState, rootGetters) { return `userCounter2:${state.userCounter2}`; } }, mutations: { // 調用:$store.commit('user2/increase') // 第一個參數是模塊的局部狀態對象state increase(state) { state.userCounter2++; } }, actions: { // 調用$store.dispatch('user2/increaseAction') // 6個參數如下 increaseAction({ commit, dispatch, state, rootState, getters, rootGetters }) { setTimeout(() => { commit('increase') }, 1000); } } } export default user2Module
index.js

import { createStore } from 'vuex'; // 導入子modules import user1 from './c_moudles/user1' import user2 from './c_moudles/user2' export default createStore({ state() { return { rootCounter: 100 } }, getters: { getInfo(state) { return `rootCounter:${state.rootCounter}`; } }, mutations: { increase(state) { state.rootCounter++; } }, actions: { increaseAction({ commit, dispatch, state, rootState, getters, rootGetters }) { setTimeout(() => { commit('increase') }, 1000); } }, modules: { user1, user2 } });
剖析補充:
A. 子模塊中增加namespaced: true,代表所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名
B. 子模塊,getters中的參數為:state, getters, rootState, rootGetters;mutations中的參數為:state;actions中的參數為:{ commit, dispatch, state, rootState, getters, rootGetters }
C. 子模塊中調用父模塊中的 mutations 或 getters,需要增加 {root:true},如下圖,其中null的位置,表示可穿參數位置。
(2). 基於$store對象進行子模塊的state、getters、mutations、actions的調用。
剖析補充:
A. 子模塊state的調用:$store.state.user1.userCounter1
B. 子模塊getters的調用:$store.getters['user1/getInfo']
C. 子模塊mutations的調用:this.$store.commit('user1/increase');
D. 子模塊actions的調用:this.$store.dispatch('user1/increaseAction');
特別注意:這里的user1代表的是父模塊導入子模塊時,modules里注冊的名稱。
上述寫法同樣是適用於CompositionApi,只不過這里的$store對象要換成store 【 const store = useStore() 】即可。
代碼分享:

<template> <div> <p>1.根模塊</p> <h4>rootCounter:{{$store.state.rootCounter}}</h4> <p>2.子模塊</p> <h4>userCounter1:{{$store.state.user1.userCounter1}}</h4> <h4>userCounter2:{{$store.state.user2.userCounter2}}</h4> <p>3.調用子模塊的getters</p> <h4>{{$store.getters['user1/getInfo']}}</h4> <h4>{{$store.getters['user2/getInfo']}}</h4> <p>4.調用父模塊的方法</p> <h4><button @click="fAdd()">加1</button></h4> <h4><button @click="fAddAction()">加1(延遲1s)</button></h4> <p>5.調用子模塊的方法</p> <h4><button @click="user1Add()">userCounter1加1</button></h4> <h4><button @click="user1AddAction()">userCounter1加1(延遲1s)</button></h4> <h4><button @click="user2Add()">userCounter2加1</button></h4> <h4><button @click="user2AddAction()">userCounter2加1(延遲1s)</button></h4> <p>6.子module中調用父module的方法</p> <h4><button @click="test()">Test</button></h4> </div> </template> <script> import {mapState} from 'vuex'; export default { methods:{ fAdd(){ this.$store.commit('increase'); }, fAddAction(){ this.$store.dispatch('increaseAction'); }, user1Add(){ this.$store.commit('user1/increase'); }, user1AddAction(){ this.$store.dispatch('user1/increaseAction'); }, user2Add(){ this.$store.commit('user2/increase'); }, user2AddAction(){ this.$store.dispatch('user2/increaseAction'); }, test(){ this.$store.dispatch('user1/fIncrease'); } } } </script> <style scoped> p{ color: #2E8B57; font-weight: bold; } </style>
二. OptionsApi_輔助函數用法
1. 說明
通過輔助函數進行子模塊的調用,主要有三種方案:
寫法1:通過完整的模塊空間名稱來查找;(寫法繁瑣,不推薦)
寫法2:第一個參數傳入模塊空間名稱,后面寫上要使用的屬性;(推薦)
寫法3:通過 createNamespacedHelpers 生成一個模塊的輔助函數;
2. 實操
公用的template代碼
<template> <div> <h4>userCounter1:{{userCounter1}}</h4> <h4>{{getInfo}}</h4> <h4><button @click="increase">user1加1</button></h4> <h4><button @click="increaseAction">user1加1(延遲1s)</button></h4> </div> </template>
寫法1:
import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; export default { computed: { ...mapState({ userCounter1: state => state.user1.userCounter1 }), ...mapGetters({ getInfo: "user1/getInfo" }) }, methods: { ...mapMutations({ increase: "user1/increase" }), ...mapActions({ increaseAction: "user1/increaseAction" }), } }
寫法2:
import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; export default { computed: { ...mapState('user1', ['userCounter1']), ...mapGetters('user1', ['getInfo']) }, methods: { ...mapMutations('user1',['increase']), ...mapActions('user1',['increaseAction']) } }
寫法3:
import { createNamespacedHelpers } from "vuex"; const { mapState, mapGetters, mapMutations, mapActions } = createNamespacedHelpers("user1") export default { computed: { ...mapState(['userCounter1']), ...mapGetters(['getInfo']) }, methods: { ...mapMutations(['increase']), ...mapActions(['increaseAction']) } }
三. CompositionApi_輔助函數用法
1. 說明
這里統一采用上述的寫法2(寫法2:第一個參數傳入模塊空間名稱,后面寫上要使用的屬性),其中useState、useGetters需要自己封裝(即:傳入模塊名稱,則調用的是子模塊中的對象,如果不傳,則調用的是父模塊),useMutations 和 useActions直接調用返回即可。
useMapper.js

import { useStore } from 'vuex' import {computed} from 'vue' /* 抽離useState和useGetters中的通用邏輯 ①:params: 需要獲取的參數名, 可以是數組,也可以是對象 分別對應兩種調用方式 ②:fn:可以是mapGetters 或 mapState */ export function useMapper(params,fn) { const store = useStore(); const storeStateFns = fn(params); const storeState={}; Object.keys(storeStateFns).forEach(fnKey=>{ const fn = storeStateFns[fnKey].bind({$store:store}); storeState[fnKey]=computed(fn); }) return storeState; }
useState.js

import { mapState, createNamespacedHelpers } from 'vuex' import { useMapper } from './useMapper' // 下面的封裝兼容了模塊的寫法哦,如果不兼容,直接一句調用即可 export function useState(moduleName, mapper) { let mapperFn = mapState if (typeof moduleName === 'string' && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapState } else { mapper = moduleName } return useMapper(mapper, mapperFn) }
useGetters.js

import { mapGetters, createNamespacedHelpers } from 'vuex' import { useMapper } from './useMapper' export function useGetters(moduleName, mapper) { let mapperFn = mapGetters if (typeof moduleName === 'string' && moduleName.length > 0) { mapperFn = createNamespacedHelpers(moduleName).mapGetters } else { mapper = moduleName } return useMapper(mapper, mapperFn) }
index.js(統一出口)
// 統一出口文件 import { useGetters } from './useGetters'; import { useState } from './useState'; export { useGetters, useState }
2. 實操
代碼分享
<template> <div> <h4>rootCounter:{{rootCounter}}</h4> <h4>userCounter1:{{userCounter1}}</h4> <h4>{{getInfo}}</h4> <h4><button @click="increase">user1加1</button></h4> <h4><button @click="increaseAction">user1加1(延遲1s)</button></h4> <h4><button @click="myIncrement">根module加1</button></h4> </div> </template> <script> import { mapMutations, mapActions } from "vuex"; import { useState, useGetters } from '../../hooks/version3' export default { setup() { //state 和 getters 需要調用自己的封裝--子modules const state1 = useState('user1', ['userCounter1']); const getters1 = useGetters('user1', ['getInfo']); // state 調用自己的封裝--根modules const fState=useState(['rootCounter']); // mutations 和 actions 調用直接返回即可 const mutations1 = mapMutations('user1', ['increase']); const actions1 = mapActions('user1', ['increaseAction']); // mutations --根modules const result2 = mapMutations({ myIncrement: 'increase', }); return { ...state1, ...getters1, ...fState, ...mutations1, ...actions1, ...result2 } } } </script>
四. 補充nextTick用法
(建議看最新總結:https://www.cnblogs.com/yaopengfei/p/16267007.html)
1. 說明
官方解釋:將回調推遲到下一個 DOM 更新周期之后執行。在更改了一些數據以等待 DOM 更新后立即使用它。
(2). 比如我們有下面的需求:
A. 點擊一個按鈕,我們會修改在h3中顯示的msg;
B. msg被修改后,獲取h3的高度;
(3). 實現上面的案例我們有三種方式:
方式一:在點擊按鈕后立即獲取到h3的高度(錯誤的做法,因為點擊的時候,h3可能還沒渲染完)
方式二:在updated生命周期函數中獲取h3的高度(但是其他數據更新,也會執行該updated回調)
方式三:使用nextTick函數;
2. 實操
代碼分享:
<template> <div> <h2>counter: {{counter}}</h2> <h4><button @click="Add">增加1</button></h4> <h3 ref="msgRef">msg:{{msg}}</h3> <h4><button @click="addMsgContent">增加Msg</button></h4> </div> </template> <script> import { ref, onUpdated, nextTick } from 'vue'; export default { setup() { const counter = ref(100); const Add = () => counter.value++; const msg = ref("hello nextTick"); const msgRef = ref(null); const addMsgContent = () => { msg.value += " 123456 "; // 更新DOM nextTick(() => { console.log(msgRef.value.offsetHeight) }) } return { counter, msg, msgRef, addMsgContent } } } </script>
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。