对Vuex进行分层

1,894 阅读2分钟

当用Vue CLI 3初始化一个Vue的项目之后,关于Vuex的内容,默认是写在 src/store.js 文件中的。

所以,我们可以对Vuex进行分层,使不同的模块区分开来。

1.改造项目

原目录是:

src/store.js

更改后的目录:

src
    -- store
        -- modules
            -- cart.js         // 购物车
            -- product.js      // 商品列表
            -- xxx.js          // 其他的,可以是 user.js,用户管理
        -- index.js            // 将原来的src/store.js移动到store目录,并重命名为 index.js
        -- mutation-types.js   // 存储一些变量的名字

2.Vuex的modules 和 namespaced

namespaced是命名空间的意思。

先建立一些变量:

mutation-types.js:

// api的列表
export const GET_PRODUCT_LIST = 'GET_PRODUCT_LIST';  // 获取商品列表

先编写两个模块:

cart.js:

// 为了防止cart.js 和product.js 中的actions相互冲突,
// 需要引入一个命名空间的概念,这样cart.js 和 product.js 中的都有用
export default {
    state: {
        carts:[]
    },
    actions: {
        
    }
}

product.js:

import * as types from '../mutation-types';
export default {
    namespaced: true,
    state: {
        products: []
    },
    actions: {
        // [types.GET_PRODUCT_LIST]的意思是当前变量去取值
        // 相当于 getProductList(){}
        [types.GET_PRODUCT_LIST]({ commit}, payload){
            console.log('xxxx');
        }
    }
}

3.在 src/store/index.js

src/store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'  // 引入其他分层的模块
import product from './modules/product'

Vue.use(Vuex)

export default new Vuex.Store({
    // 如果需要引入模块中的状态,需要用到 modules
    modules: {
        cart,
        product
    },
    state: {
        // xxx
    },
    mutations: {
        
    },
    actions: {
        
    }
})

4. 在组件中触发actions中的方法

在Home.vue组件中,去触发actions中的方法。

一般在actions里面,去请求后台接口。也就是说,把请求接口的方法,写在actions里面。然后在组件中去触发。

(1)第一种触发actions的方式:使用this.$store.dispatch方法

Home.vue:

<template>
    <div class="home">
        商品页
    </div>
</template>

<script>
import * as types from '../store/mutation-types'
export default {
    mounted(){
        // 1.第一种触发actions的方式:使用this.$store.dispatch方法
        // 触发actions
        // 'product/' + types.GET_PRODUCT_LIST是指定product.js模块下的actions方法
        this.$store.dispatch('product/'+ types.GET_PRODUCT_LIST)
    }
}
</script>

(2) 第二种触发actions的方式:使用 mapActions

Home.vue:

<template>
    <div class="home">
        商品页
    </div>
</template>

<script>
import { mapActions } from 'vuex'
import * as types from '../store/mutation-types'
export default {
    methods: {
        // 'product' 指的是product.js 这个模块
        // [type.GET_PRODUCT_LIST] 指的是变量取值
        ...mapActions('product', [types.GET_PRODUCT_LIST])
    },
    mounted(){
        // 2.第二种触发actions的方式:使用 mapActions
        this[types.GET_PRODUCT_LIST]()
    }
}
</script>

(3)第三种方法:createNamespaceHelpers,创建命名空间的帮助方法

Home.vue:

<template>
    <div class="home">
        商品页
    </div>
</template>

<script>
import { createNamespacedHelpers } from 'vuex'
let { mapActions }  = createNamespacedHelpers('product')  // 创建product的命名空间
import * as types from '../store/mutation-types'
export default {
    methods: {
        // 配合createNamespacedHelpers帮助方法使用
        // 已经指定命名空间,不需要在...mapActions()中指定
        ...mapActions([types.GET_PRODUCT_LIST])
    },
    mounted(){
        // 2.第二种触发actions的方式:使用 mapActions
        this[types.GET_PRODUCT_LIST]()
    }
}
</script>

5. getters

getters,类似于computed

state和getters都属于 Vuex的属性。
比如,我们要把state状态的数据进行一次映射或者筛选,再把这个结果重新计算并提供组件使用。举个例子:

store.js:

state: {
     arr: [
       {id: 1, name: 'iissoft',score: 80},
       {id: 2, name: 'steven', score:60},
       {id: 3, name: 'jerry', score:90}
     ]
},
getters: {
    arrList: function (state) { //这里我们对状态进行映射,进行重新计算
         return state.arr.map(function(item){
               return item.score >= 60 ? '及格' : '不及格';
         })
    }
}

此时,getters会暴露一个store.getters 对象,我们就可以在任何组件中使用 this.$store.getters.xxx来绑定数据。

在组件中调取getters中的数据。 header.vue:

<template>
     <div>
          <ul>
              <li v-for="item in arrList">{{item}}</li>
          </ul>
     </div>
</template>
<script>
     export default {
         computed: {
	      arrList: function (){
                     return this.$store.getters.arrList;  //通过store.getters对象来访问
              }
         }
     }
</script>

另一种方式,来调取getters中的数据。

Vuex给我们提供了另一个方法mapGetters。

store.js:

state: {
    arr: [  //状态
        {id: 1, name: 'iissoft', score: 80 },
        {id: 2, name: 'steven', score: 60},
        {id: 3, name: 'jerry', score: 90}
    ]
},
getters: {  //这里我们使用es6新语法箭头函数
    arrList: state => state.arr.map(item => item.score >= 60 ? '及格':'不及格')
}

在组件中使用 mapGetters来调取 getters中的数据。

结合 ...对象运算符来合并我们组件的本地计算属性。

header.vue:

<template>
    <div>
        <ul>
            <li v-for="item in arrList">{{item}}</li>
        </ul>
    </div>
</template>
<script>
    import { mapGetters } from 'vuex'
    export default {
        computed: {
            ...mapGetters(['arrList'])
        }
    }
</script> 

最后如果我们想给getters属性起个别名,我们可以通过对象的形式:

<template>
    <div>
        <ul>
            <li v-for="item in newArr">{{item}}</li>
        </ul>
    </div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
    computed: {
        ...mapGetters({
            newArr: 'arrList'  //给getters属性arrList起个别名newArr
        })
    }
}
</script>

6. 在mutation里面去更改状态

(1) 首先在 mutation-types.js 中去编写变量名

mutation-types.js:

// actions的变量名

// 得到短信验证码
export const DO_GET_VERIFY_CODE = 'DO_GET_VERIFY_CODE'

// mutations的变量名

// 更改公共状态codeValue
export const SET_CODE_VALUE = 'SET_CODE_VALUE'

(2) 然后去分层的模块中,去编写 actions 和 mutations 中的方法

modules/verifyCode.js:

// 引入变量名
import * as types from '../mutation-types'

// 引入接口方法
import { getVerifyCode } from '../../api/verifyCode/verifyCode'

const verifyCode = {
  state: {
    codeValue: '1234'
  },
  // 类似于computed
  // getters: {},
  mutations: {
    // 在mutations 里面去更改状态
    [types.SET_CODE_VALUE] (state, { data }) {
      state.codeValue = data // 更改
    }
  },
  //一般在actions中,去编写使用接口的方法
  //然后,在组件中去调用 actions 中的方法
  //通过this.$store.dispatch() 方法去调用
  //如果不用this.$store.dispatch()去调用
  //可以使用 mapActions 去调用
  actions: {
    // toGetVerifyCode ({ commit }, payload) {
    //   // 去使用api的接口方法
    // }

    // [types.DO_GET_VERIFY_CODE]的意思是当前变量去取值
    // 相当于 doGetVerifyCode(){}
    // [types.DO_GET_VERIFY_CODE] ({ commit }, payload) {
    //   console.log('xxx')
    // }

    [types.DO_GET_VERIFY_CODE] ({ commit, state }, payload) {
      console.log('xxx')
      getVerifyCode().then(data => {
        console.log(data.data)
        console.log(data.data.data)
        // 提交给mutations的方法
        commit('SET_CODE_VALUE', { data: data.data.code })
      })
    }
  },
  // namespaced: {}
  namespaced: true
}

export default verifyCode

7. 统一使用 [types.DO_GET_VERIFY_CODE] 这种写法

在 Vuex 中,以及组件中,都可以使用 [types.DO_GET_VERIFY_CODE] 这种写法。

7.1 在Vuex中

(1) 首先在 mutation-types.js 中去编写变量名

mutation-types.js:

// actions的变量名

// 得到短信验证码
export const DO_GET_VERIFY_CODE = 'DO_GET_VERIFY_CODE'

// mutations的变量名

// 更改公共状态codeValue
export const SET_CODE_VALUE = 'SET_CODE_VALUE'

(2) 然后去分层的模块中,去编写 actions 和 mutations 中的方法

modules/verifyCode.js:

// 引入变量名
import * as types from '../mutation-types'

// 引入接口方法
import { getVerifyCode } from '../../api/verifyCode/verifyCode'

const verifyCode = {
  state: {
    codeValue: '1234'
  },
  // 类似于computed
  // getters: {},
  mutations: {
    // 在mutations 里面去更改状态
    [types.SET_CODE_VALUE] (state, { data }) {
      state.codeValue = data // 更改
    }
  },
  //一般在actions中,去编写使用接口的方法
  //然后,在组件中去调用 actions 中的方法
  //通过this.$store.dispatch() 方法去调用
  //如果不用this.$store.dispatch()去调用
  //可以使用 mapActions 去调用
  actions: {
    // toGetVerifyCode ({ commit }, payload) {
    //   // 去使用api的接口方法
    // }

    // [types.DO_GET_VERIFY_CODE]的意思是当前变量去取值
    // 相当于 doGetVerifyCode(){}
    // [types.DO_GET_VERIFY_CODE] ({ commit }, payload) {
    //   console.log('xxx')
    // }

    [types.DO_GET_VERIFY_CODE] ({ commit, state }, payload) {
      console.log('xxx')
      getVerifyCode().then(data => {
        console.log(data.data)
        console.log(data.data.data)
        // 提交给mutations的方法
        commit('SET_CODE_VALUE', { data: data.data.code })
      })
    }
  },
  // namespaced: {}
  namespaced: true
}

export default verifyCode

7.2 最后,在组件中,去调用 actions 中的方法

views/order_details/order_details.vue:

<template>
    <div>
        获取验证码 {{codeValue}}
    </div>
</template>
<script>
// 在组件中,去触发Vuex中的actions的方法
// createNamespacedHelpers,是创建命名空间的帮助方法
import { createNamespacedHelpers } from 'vuex' // 创建verifyCode的命名空间
import * as types from '../../store/mutation-types'
// 只使用verifyCode模块中的actions 和 state
let { mapActions, mapState } = createNamespacedHelpers('verifyCode')

// import { mapActions, mapState } from 'vuex'
// import * as types from '../../store/mutation-types'

export default {
  name: 'order_details',
  data () {
    return {

    }
  },
  computed: {
    // 获取Vuex中的公共状态的值
    ...mapState(['codeValue'])
  },
  mounted () {
    this.init()

    // 页面加载完的时候,去使用这个方法
    // 2.第二种触发actions的方法:使用 mapActions
    this[types.DO_GET_VERIFY_CODE]()
  },
  methods: {
    // 配合createNamespacedHelpers帮助方法使用
    // 已经指定命名空间,不需要在...mapActions()中指定
    ...mapActions([types.DO_GET_VERIFY_CODE]),

    init () {
      // 页面一加载,就请求网银支付的银行卡接口

      // 页面一加载的时候,就去执行
      this.$nextTick(() => {
        console.log(1232)
        console.log(this.$refs.radioGroup1)
      })
    }
  }
}
</script>

8. 关于 getters

getters 类似于 computed。

对 state 里面的数据二次处理(对数据进行过滤筛选,类似于 filter 的作用)

在 getters 中对 Vuex 顶层数据进行过滤, 而不改动 state 里原本的数据。

const getters = {
    showSidebar: state => state.showSidebar
}

在组件中去获取 getters 中的数据。

可以使用 mapGetters 。

9. 使用 createNamespacedHelpers,创建命名空间的帮助方法,的情况

views/order_details/order_details.vue:

<template>
    <div>
        获取验证码 {{codeValue}}
    </div>
</template>
<script>
// 在组件中,去触发Vuex中的actions的方法
// createNamespacedHelpers,是创建命名空间的帮助方法
import { createNamespacedHelpers } from 'vuex' // 创建verifyCode的命名空间
import * as types from '../../store/mutation-types'
// 只使用verifyCode模块中的actions 和 state
let { mapActions, mapState } = createNamespacedHelpers('verifyCode')

export default {
  name: 'order_details',
  data () {
    return {

    }
  },
  computed: {
    // 获取Vuex中的公共状态的值
    ...mapState(['codeValue'])
  },
  mounted () {
    this.init()

    // 页面加载完的时候,去使用这个方法
    // 2.第二种触发actions的方法:使用 mapActions
    this[types.DO_GET_VERIFY_CODE]()
  },
  methods: {
    // 配合createNamespacedHelpers帮助方法使用
    // 已经指定命名空间,不需要在...mapActions()中指定
    ...mapActions([types.DO_GET_VERIFY_CODE]),

    init () {
      // 页面一加载,就请求网银支付的银行卡接口

      // 页面一加载的时候,就去执行
      this.$nextTick(() => {
        console.log(1232)
        console.log(this.$refs.radioGroup1)
      })
    }
  }
}
</script>

前提是在分层的模块中,加上 namespaced: true,使其成为带命名空间的分层的模块。

例如:

modules/verifyCode.js:

// 引入变量名
import * as types from '../mutation-types'

// 引入接口方法
import { getVerifyCode } from '../../api/verifyCode/verifyCode'

const verifyCode = {
  state: {
    codeValue: '1234'
  },
  mutations: {
      //在mutations里面去更改状态
  },
  actions: {
      //去使用api的接口方法
  },
  namespaced: true
}
export default verifyCode

10. 没有使用 createNamespacedHelpers,创建命名空间的帮助方法,的情况

views/order_details/order_details.vue:

<template>
    <div>
        获取验证码 {{codeValue}}
    </div>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import * as types from '../../store/mutation-types'

export default {
  name: 'order_details',
  data () {
    return {

    }
  },
  computed: {
    // 获取Vuex中的公共状态的值
    ...mapState(['codeValue'])
  },
  mounted () {
    this.init()

    // 页面加载完的时候,去使用这个方法
    // 2.第二种触发actions的方法:使用 mapActions
    this[types.DO_GET_VERIFY_CODE]()
  },
  methods: {
    // 没有配合createNamespacedHelpers帮助方法使用
    // 需要在...mapActions()中指定
    // 在辅助函数mapActions的第一参数上,填写上模块的命名空间名。
    ...mapActions('verifyCode', [types.DO_GET_VERIFY_CODE]),

    init () {
      // 页面一加载,就请求网银支付的银行卡接口

      // 页面一加载的时候,就去执行
      this.$nextTick(() => {
        console.log(1232)
        console.log(this.$refs.radioGroup1)
      })
    }
  }
}
</script>

前提是在分层的模块中,加上 namespaced: true,使其成为带命名空间的分层的模块。

例如:

modules/verifyCode.js:

// 引入变量名
import * as types from '../mutation-types'

// 引入接口方法
import { getVerifyCode } from '../../api/verifyCode/verifyCode'

const verifyCode = {
  state: {
    codeValue: '1234'
  },
  mutations: {
      //在mutations里面去更改状态
  },
  actions: {
      //去使用api的接口方法
  },
  namespaced: true
}
export default verifyCode

11. 一个完整的 Vuex 的实际案例

12.参考

vuex中module的命名空间概念