天天看點

Vue初學習之狀态管理-Modules

作者:甯靜知行者

通過文章的學習,你可以了解以下内容

  1. 如何進行Vue modules的定義
  2. 如何調用不同module的内容
  3. 重構現有未分module的代碼

前言

在前面的章節中,我們都是使用一個store對象,如果不同的業務功能,都需要進行狀态聲明,那這個對象就會變得很大,也不好管理,這時就需要将 store進行子產品管理。

定義

const module1 = {
  state: ()=>({}),
  mutations: {},
  actions: {},
  getters: {}
}
const module2 = {
  state: ()=>({}),
  mutations: {},
  actions: {},
  getters: {}
}

// 建立多子產品的Store
var store = new Vuex.Store({
  modules: {
    one: module1,
    two: module2
  }
})

// 元件中通路
store.state.one // one子產品的狀态通路
store.state.two // two子產品的狀态通路           

子產品局部狀态

const module1 = {
  state: () => ({
    name: ""
  }),
  mutations: {
    modifyName(state, name) {
      state.name = name;
    }
  },
  actions: {
    modifyName({ state, commit, rootState }, name) {
      if (rootState.name == 'test')
        commit('modifyName', name);
    }
  },
  getters: {
    getName(state,getters,rootState) {
      return state.name;
    }
  }
}           
  1. 對于子產品内部的 mutation 和 getters,接收的第一個參數是子產品的局部狀态對象
  2. 對于局部狀态通過 context.state 進行暴露,根節點狀态則通過 context.rootState通路
  3. 對于 getters,則通過第三個參數 rootState 進行根節點的暴露

命名空間

預設情況下,子產品内部的 action、mutation和getter 是注冊在全局命名空間的。如果想讓子產品具有更高的封裝度和複用性,可以指定 namespaced: true 使其成為帶命名子產品

const module1 = {
  namespaced: true,
  state: () => ({
    name: ""
  }),
  mutations: {
    modifyName(state, name) {
      state.name = name;
    }
  },
  actions: {
    modifyName({ state, commit, rootState }, name) {
      if (rootState.name == 'test')
        commit('modifyName', name);
    }
  },
  getters: {
    getName(state) {
      return state.name;
    }
  },
  // 嵌套子產品
  modules: {
    aOne: { // 繼承父命名空間
      state: () => ({
        achild: ""
      }),
      getters: {
        getAchild(state) {
          return state.achild;
        }
      }
    },
    // 嵌套命名空間
    aTwo: {
      namespaced: true,
      state: () => ({
        aTwoChild: ""
      }),
      getters: {
        getAtwo(state) {
          return state.aTwoChild;
        }
      }
    }
  }
}

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

store.state.a // => 通路a子產品的狀态
store.getters.a.getAchild
store.getters.a.aTwo.getAtwo //            

在帶命名空間子產品内通路全局内容

全局的 state 和 getter 會 以rootState 和 rootGetters 作為第三個和第四個參數傳入 getter ,同時也會通過 context 對象的屬性傳入action。

如果想在全局命名空間分發action 或送出mutation ,将 { root: true} 作為第三個參數傳給 dispatch 或是 commit

const module1 = {
  namespaced: true,
  state: () => ({
    name: ""
  }),
  mutations: {
    modifyName(state, name) {
      state.name = name;
    }
  },
  actions: {
    modifyName({ state, dispatch, commit, rootState }, name) {
      if (rootState.name == 'test')
        commit('modifyName', name); // 子產品局部muation
      dispatch('moduleAction');
      dispatch('rootAction', null, { root: true }) // 全局 rootAction
      commit('rootMutation', null, { root: true }) // 全局mutation


    }
  },
  getters: {
    getName(state, getters, rootState, rootGetters) {
      return state.name;
    }
  }
}           

當在多子產品中,需要進行調用指定子產品的方法,通過 store.dispatch("namespace/method")的方式進行調用,如果是全局的,則直接調用store.dispatch("method")

實踐

通過以上的了解,接下來我們來定義一個兩個子產品 app 和 user ,來實踐下

  • 定義 app.js 和 user.js 定義兩個子產品的内容
  • 建立 store 目錄,結構如下
Vue初學習之狀态管理-Modules
  • 在store 目錄下建立modules,建立 user.js 和 app.js, 内容如下
// app.js
const state = {
    appId: "",
    title: ""
}

const mutations = {
    SET_TITLE: (state, title) => {
        state.title = title
    },
    SET_APPID: (state, appId) => {
        state.appId = appId
    }
}

const actions = {
    setTitle({ commit }, title) {
        commit('SET_TITLE', title);
    },
    setAppId({ commit }, appId) {
        commit('SET_APPID', appId)
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}           
// user.js
const state = {
    id: "",
    name: ""
}

const mutations = {
    SET_ID: (state, id) => {
        state.id = id
    },
    SET_NAME: (state, name) => {
        state.name = appId
    }
}

const actions = {
    setID({ commit }, id) {
        commit('SET_ID', id);
    },
    setName({ commit }, name) {
        commit('SET_NAME', name)
    }
}

export default {
    namespaced: true,
    state,
    mutations,
    actions
}
           
  • 在 store 目錄下建立 index.js 檔案,内容如下
import Vue from 'vue'
import Vuex from 'vuex';
import app from './modules/app'
import user from './modules/user'

Vue.use(Vuex)

// 建立兩個子產品
const store = new Vuex.Store({
    modules: {
        app,
        user
    }
})

export default store           
  • 導入 store 到 vue 執行個體中
import store from './store'

new Vue({ data: data, render: h => h(App), store }).$mount("#app");           
  • 擷取store資料
<template>
  <div class="hello">
    <h1>input text {{ inputValue }}</h1>
    <button @click="setAppName">修改appName</button>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    setAppName() {
      this.count++;
      // 調用指定子產品下的方法
      this.$store.dispatch("app/setTitle", this.count);
    },
  },
  computed: {
    // 設定計算屬性
    inputValue() {
      // 傳回app子產品下的狀态資料
      return this.$store.state.app.title;
    },
  },
};
</script>           

總結

命名空間是為了解耦 store的單一狀态,各司其職的劃分子產品,可以提升代碼的擴充性和可維護性

繼續閱讀