1. 程式人生 > >【15】vuex2.0 之 modules

【15】vuex2.0 之 modules

his 來看 暴露 方式 ets 我們 spa web space

  vue 使用的是單一狀態樹對整個應用的狀態進行管理,也就是說,應用中的所有狀態都放到store中,如果是一個大型應用,狀態非常多, store 就會非常龐大,不太好管理。這時vuex 提供了另外一種方式,可以把整個store 分成幾個大的模塊,如登錄模塊,用戶模塊等,每一個模塊都有自己的state, mutation, actions ,getters , 它就相當於是一個小的store,然後我們的根store(通過new Vuex.Store 生成的store) 通過它的modules屬性引入這些模塊,從而我們的組件就可以使用這些modules 中狀態(state).

  新建一個項目體驗一下,通過vue –cli新建一個項目vuemodule, 不要忘記安裝vuex.

  1, 在src 目錄下新一個login文件夾,在裏面新建index.js 用於存放login 模塊的狀態。 為了簡單起見,我把模塊下的state, actions,mutations, getters 全放在index.js文件中。

先簡單給它增加一個狀態,userName: “sam”

技術分享
const state = {
    useName: "sam"
};
const mutations = {

};
const actions = {

};
const getters = {

};

// 不要忘記把state, mutations等暴露出去。
export default {
    state,
    mutations,
    actions,
    getters
}
技術分享

  2,在src 目錄下,再新建一個 store.js 這是根store, 它通過modules 屬性引入 login模塊。

技術分享
import Vue from "vue";
import Vuex from "Vuex";

Vue.use(Vuex);

// 引入login 模塊
import login from "./login"

export default new Vuex.Store({
    // 通過modules屬性引入login 模塊。
    modules: {
        login: login
    }
})
技術分享

  3, 在main.js中引入store, 並註入到vue 根實例中。

技術分享
import Vue from ‘vue‘
import App from ‘./App.vue‘

// 引入store
import store from "./store"

new Vue({
  el: ‘#app‘,
  store,  // 註入到根實例中。
  render: h => h(App)
})
技術分享

  4,在 app.vue 中通過computed屬性獲取到login下的state. 這裏要註意,在沒有modules 的情況下,組件中通過 this.$store.state.屬性名 可以獲取到,但是有modules 之後,state 被限制到login 的命名空間(模塊)下,所以屬性名前面必須加模塊名(命名空間),組件中通過 this.$store.state.模塊名.屬性名,在這裏是 this.$store.state.login.userName

技術分享
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>
  </div>
</template>

<script>
export default {
  // computed屬性,從store 中獲取狀態state,不要忘記login命名空間。
  computed: {
    useName: function() {
      return this.$store.state.login.useName
    }
  }
}
</script>
技術分享

組件中成功獲取到狀態。項目目錄和展示如下

技術分享

  4 ,通過actions, mutations 改變名字, 這就涉及到dispatch action, commit mutations, mutations 改變state.

  先在login 文件夾 index.js中添加changeName action 和 CHANGE_NAME mutations.

技術分享
const mutations = {
    CHANGE_NAME (state, anotherName) {
        state.useName = anotherName;
    }
};

const actions = {
    changeName ({commit},anotherName) {
        commit("CHANGE_NAME", anotherName)
    }
};
技術分享

  在app.vue 中添加一個按鈕:<button> change to json</button>, 點擊時,dispatch 一個 action. 那在組件中怎麽dispatch actions 呢?

  在模塊中,state 是被限制到模塊的命名空間下,需要命名空間才能訪問。 但actions 和mutations, 其實還有 getters 卻沒有被限制,在默認情況下,它們是註冊到全局命名空間下的,所謂的註冊到全局命名空間下,其實就是我們訪問它們的方式和原來沒有module 的時候是一樣的。比如沒有module 的時候,this.$store.dispatch(“actions”), 現在有了modules, actions 也寫在了module 下面(changeName 寫到了login目錄下的index.js中),我們仍然可以這麽寫,this.$store.dispatch(“changeName”), 組件中的getters, 也是通過 this.$store.getters.module中getters 來獲取。

技術分享
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>
    <!-- 添加按鈕 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
export default {
  // computed屬性,從store 中獲取狀態state,不要忘記login命名空間。
  computed: {
    useName: function() {
      return this.$store.state.login.useName
    }
  },
  methods: {

  // 和沒有modules的時候一樣,同樣的方式dispatch action
    changeName() {
      this.$store.dispatch("changeName", "Jason")
    }
  }
}
技術分享

  5, 局部參數

  雖然dispatch action和 commit mutations 可以全局使用,但是寫在module 中的actions, mutations 和getters, 它們獲得的默認參數卻不是全局的,都是局部的,被限定在它們所在的模塊中的。比如mutations和getters 會獲得state 作為第一個默認參數,這個state參數,就是限定在mutations 和getters 所在模塊的state對象,login 文件夾下的mutations 和getters 只會獲取到當前index.js 中的 state 作為參數 。 actions 會獲得一個context 對象作為參數,這個context 對象就是當前module 的實例,module 相當於一個小store.

  那麽怎樣才能獲取到根store 中的state 和 getters 呢? Vuex 提供了 rootState, rootGetters 作為module 中 getters 中默認參數, actions中context 對象,也會多了兩個屬性,context.getters, context. rootState, 這些全局的默認參數,都排在局部參數的後面。

  我們在store.js中添加 state, getters:

技術分享
export default new Vuex.Store({
    // 通過modules屬性引入login 模塊。
    modules: {
        login: login
    },

    // 新增state, getters
    state: {
        job: "web"
    },
    getters: {
        jobTitle (state){
            return state.job + "developer"
        }
    }
})
技術分享

  login 目錄下的 index.js

技術分享
const actions = {
    // actions 中的context參數對象多了 rootState 參數
    changeName ({commit, rootState},anotherName) {
        if(rootState.job =="web") {
            commit("CHANGE_NAME", anotherName)
        }
    }
};

const getters = {
    // getters 獲取到 rootState, rootGetters 作為參數。
    // rootState和 rootGetter參數順序不要寫反,一定是state在前,getter在後面,這是vuex的默認參數傳遞順序, 可以打印出來看一下。
    localJobTitle (state,getters,rootState,rootGetters) {  
        console.log(rootState);
        console.log(rootGetters);
        return rootGetters.jobTitle + " aka " + rootState.job 
    }
};
技術分享

  app.vue 增加h2 展示 loacaJobTitle, 這個同時證明了getters 也是被註冊到全局中的。

技術分享
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{useName}}</h1>

    <!-- 增加h2 展示 localJobTitle -->
    <h2>{{localJobTitle}}</h2>
    <!-- 添加按鈕 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  // computed屬性,從store 中獲取狀態state,不要忘記login命名空間。
  computed: {
    ...mapState({
      useName: state => state.login.useName
    }),

    // mapGeter 直接獲得全局註冊的getters
    ...mapGetters(["localJobTitle"])
  },
  methods: {
    changeName() {
      this.$store.dispatch("changeName", "Jason")
    }
  }
}
</script>
技術分享

  6, 其實actions, mutations, getters, 也可以限定在當前模塊的命名空間中。只要給我們的模塊加一個namespaced 屬性:

技術分享
const state = {
    useName: "sam"
};
const mutations = {
    CHANGE_NAME (state, anotherName) {
        state.useName = anotherName;
    }
};
const actions = {
    changeName ({commit, rootState},anotherName) {
        if(rootState.job =="web") {
            commit("CHANGE_NAME", anotherName)
        }
    },
    alertName({state}) {
        alert(state.useName)
    }
};
const getters = {
    localJobTitle (state,getters,rootState,rootGetters) {  
        return rootGetters.jobTitle + " aka " + rootState.job 
    }
};
// namespaced 屬性,限定命名空間
export default {
    namespaced:true,
    state,
    mutations,
    actions,
    getters
}
技術分享

  當所有的actions, mutations, getters 都被限定到模塊的命名空間下,我們dispatch actions, commit mutations 都需要用到命名空間。如 dispacth("changeName"), 就要變成 dispatch("login/changeName"); getters.localJobTitle 就要變成 getters["login/localJobTitle"]

  app.vue 如下:

技術分享
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1 @click ="alertName">{{useName}}</h1>

    <!-- 增加h2 展示 localJobTitle -->
    <h2>{{localJobTitle}}</h2>
    <!-- 添加按鈕 -->
    <div>
      <button @click="changeName"> change to json</button>
    </div>
  </div>
</template>

<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  // computed屬性,從store 中獲取狀態state,不要忘記login命名空間。
  computed: {
    ...mapState("login",{
      useName: state => state.useName
    }),

     localJobTitle() {
       return this.$store.getters["login/localJobTitle"]
     }
  },
  methods: {
    changeName() {
      this.$store.dispatch("login/changeName", "Jason")
    },
    alertName() {
      this.$store.dispatch("login/alertName")
    }
  }
}
</script>
技術分享

  有了命名空間之後,mapState, mapGetters, mapActions 函數也都有了一個參數,用於限定命名空間,每二個參數對象或數組中的屬性,都映射到了當前命名空間中。

技術分享
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
  computed: {
    // 對象中的state 和數組中的localJobTitle 都是和login中的參數一一對應。
    ...mapState("login",{
      useName: state => state.useName
    }),
    ...mapGetters("login", ["localJobTitle"])
  },
  methods: {
    changeName() {
      this.$store.dispatch("login/changeName", "Jason")
    },
    ...mapActions(‘login‘, [‘alertName‘])
  }
}
</script>
技術分享

 

【15】vuex2.0 之 modules