vuex的模块化使用

225 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第31天,点击查看活动详情

前面我们介绍了vuex的组成部分,今天这篇文章介绍一下vuex模块化的使用。

辅助函数

首先我们先来介绍几个辅助函数: mapState, mapGetters, mapMutations, mapActions。可以发现它们有一个共同点,就是map+组成部分,这些辅助函数的主要目的就是帮助我们快速获取对应部分的属性,这样在开发中就能省去很多重复代码,使用上更加简单。

mapState

mapState就是状态state的辅助函数。

直接访问state

在这之前提醒一下,安装的vuex版本一定要和vue版本对应,vue3里面可以使用最新版的vuex,但是在vue2中不要安装最新版本的vuex,否则使用store的时候会报undefined,也就是说如果版本安装不对应,没法使用store的时候会报undefined, 也就是说如果版本安装不对应,没法使用store哦,一定要注意哦

<div>获取vuex中的a: {{ $store.state.a }}</div>
<div>获取vuex中的b: {{ $store.state.b }}</div>
<div>获取vuex中的c: {{ $store.state.c }}</div>

如果组件中有很多地方都使用到state里的值,那么页面上就会出现很多$store.state,这样模板看起来就很不简洁,使用下面的辅助函数mapState就可以避免这种问题了呀。

使用mapState辅助函数

mapState函数是帮助我们生成计算属性的. 基础用法 当计算属性的名称和state里面的名称相同的时候,可以传入字符串数组的形式。

computed: {
  mapState(['a'])
}

mapState返回的是一个对象,如果需要将多个对象合并为一个,可以使用对象展开运算符。

computed: {
  ...mapState(['a', 'b', 'c'])
}

实例应用 上面代码改写成mapState,可以写成这样

<template>
  <div class="hello">
    <div>获取vuex中的a: {{ a }}</div>
    <div>获取vuex中的b: {{ b }}</div>
    <div>获取vuex中的c: {{ c }}</div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'HelloWorld',
  data () {
    return {
    }
  },
  computed: {
    ...mapState(['a', 'b', 'c'])
  },
  methods: {
  }
}
</script>

效果如下:

企业微信截图_ac1acd94-083a-4d0c-9139-14dbd0f8a0fc.png

mapGetters

mapGetters是getter的辅助函数。是将store中的getter映射到局部计算属性。

定义下面的这种getter

const getters = {
  odds: state => {
    return state.d.filter(item => item % 2 !== 0)
  }
}

直接获取getter

<template>
  <div class="hello">
    <div>获取vuex中的a: {{ a }}</div>
    <div>获取vuex中的b: {{ b }}</div>
    <div>获取vuex中的c: {{ c }}</div>
    <div>
      获取vuex中的d
      <div v-for="(item, index) in $store.getters.odds" :key="index">
        {{item}}
      </div>
    </div>
  </div>
</template>

效果如下:

企业微信截图_54313e2c-3a55-45e0-a0e3-814a65d4f3ff.png

使用mapGetters辅助函数

mapGetters函数也是要在计算属性中使用的。 基本用法 如果想给getter属性另取一个名字,就使用对象形式

computed: {
  ...mapGetters({
    //`this.oddVal` 映射为 `this.$store.getters.odds`
    oddVal: 'odds'
  })
},

如果名字和属性名相同,那么就传入一个数组即可

computed: {
  ...mapGetters(['odds'])
},

实例应用

<template>
  <div class="hello">
    <div>获取vuex中的a: {{ a }}</div>
    <div>获取vuex中的b: {{ b }}</div>
    <div>获取vuex中的c: {{ c }}</div>
    <div>
      获取vuex中的d
      <div v-for="(item, index) in odds" :key="index">
        {{item}}
      </div>
    </div>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'
export default {
  name: 'HelloWorld',
  data () {
    return {
    }
  },
  computed: {
    ...mapState(['a', 'b', 'c']),
    ...mapGetters(['odds'])
  },
  methods: {
  }
}
</script>

mapMutations

mapMutations是mutation的辅助函数。

定义一个mutation

const mutations = {
  increment (state, value) {
    state.a += value
  }
}

直接使用mutations

<button @click="addA">增加a</button>
methods: {
  addA () {
    this.$store.commit('increment', 2)
  }
}

效果如下:

企业微信截图_dc472278-7720-4089-8860-9c4518fb5e16.png

使用mapMutations辅助函数

mapMutations是mutations的辅助函数。 基本用法 将this.increment映射成this.$store.commit('increment')

methods: {
  ...mapMutations(['increment'])
}

将this.addA映射成this.$store.commit('increment')

...mapMutations({
  addA: 'increment'
})

如何需要支持负荷,直接在使用increment的地方使用()传参即可。

methods: {
  ...mapMutations(['increment'])
}

使用mapMutations辅助函数

<button @click="increment">增加a</button>
methods: {
  ...mapMutations(['increment', '2'])
}

但是这时候存在一个问题,此时拼接的时候不是相加,而是将事件当做字符串拼接起来了。如图所示:

企业微信截图_b4d1caf8-e4ae-46e4-aba9-69f8854fd02d.png

因为此时'2'是作为负载对象传进去了,如果想要传参数的时候,直接在调用的时候使用()传递即可。

<button @click="increment(2)">增加a</button>
methods: {
  ...mapMutations(['increment'])
}

mapActions

mapActions是actions的辅助函数,actions也可以用来修改状态,和mutations相比,支持异步函数。

定义一个actions

const actions = {
  increment (context, value) {
    context.commit('increment', value)
  }
}

直接使用actions

<button @click="addA">增加a</button>
addA () {
  this.$store.dispatch('increment', 4)
}

使用mapActions辅助函数

基本用法 它的用法和mapMutation的用法一模一样,只是映射的是actions

将this.increment映射成this.$store.dispatch('increment')

methods: {
  ...mapActions(['increment'])
}

将this.addA映射成this.$store.dispatch('increment')

...mapActions({
  addA: 'increment'
})

如何需要支持负荷,直接在使用increment的地方使用()传参即可。

methods: {
  ...mapActions(['increment'])
}

实例

<button @click="increment(4)">增加a</button>
methods: {
  ...mapMutations(['increment']),
  ...mapActions(['increment'])
}

模块化使用vuex

在实际项目开发的时候,往往业务很复杂,我们需要按照功能模块区分state,因此模块化使用vuex在实际开发的时候是十分重要的,一般有两种形式:

  • 使用module属性配置模块
  • 从项目目录开始模块化

使用module属性配置模块

这种方式非常简单,vuex给我们提供了module属性,我们只需要将我们的模块化内容配置在这里即可。

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
const moduleA = {
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    odds: state => {
      return state.d.filter(item => item % 2 !== 0)
    }
  },
  mutations: {
    ncrement (state, value) {
      state.a += value
    }
  },
  actions: {
    increment (context, value) {
      context.commit('increment', value)
    }
  }
}
const moduleB = {
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    evens: state => {
      return state.d.filter(item => item % 2 === 0)
    }
  },
  mutations: {
    add (state, value) {
      state.a += value
    }
  },
  actions: {
    add (context, value) {
      context.commit('add', value)
    }
  }
}

export default new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

如果还有一些公共的state, getters, mutations, actions那么也是可以配置在里面的,否则可以不配置

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: {
    a: moduleA,
    b: moduleB
  }
})

上面这段代码中不同模块之间最终会合并,会校验命名,不允许模块之间存在相同名称,否则会报错。 保证不同名称之间互不烦扰,那么使用方式就是和以前一样的。

命名空间

如果觉得修改命名,让不同模块之间的命名不同很麻烦,vuex提供了一个属性namespaced,如果在模块中配置了这个属性为true,那么就说明只要保证当前模块内部不重名就可以,不用管模块之间。因为 模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名,这样就不可能重名了。

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
const moduleA = {
  namespaced: true,
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    odds: state => {
      return state.d.filter(item => item % 2 !== 0)
    }
  },
  mutations: {
    increment (state, value) {
      state.a += value
    }
  },
  actions: {
    increment (context, value) {
      context.commit('increment', value)
    }
  }
}
const moduleB = {
  namespaced: true,
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    odds: state => {
      return state.d.filter(item => item % 2 === 0)
    }
  },
  mutations: {
    increment (state, value) {
      state.a += value
    }
  },
  actions: {
    increment (context, value) {
      context.commit('increment', value)
    }
  }
}

export default new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

这时候按照之前的调用方式页面就会报错(unknown action type: increment) 这是因为添加了命名空间后,页面按照原来的方式已经找不到我们定义的东西了。 state和mapState 从state的打印信息来看,也是需要加上模块名的

企业微信截图_553ef14b-7ada-4295-b858-9da5c89ddaa4.png

this.$store.state.a.a

import {mapState} from 'vuex'
 computed:{
 	...mapState('a',['a','b', 'c'])
 }

getters和mapGetters 对于getters这类,需要加上模块名。因为有模块之后,getters内容如下:

企业微信截图_3cb6d42b-ee88-4d61-b0ff-a58d11967576.png

this.$store.getters['a/odds']

...mapGetters({
  odds: 'a/odds'
})

mutations和mapMutations 从下面打印信息可以看出来, mutations内容如下:

企业微信截图_09810da9-fb68-4470-8cbb-a72af7a74690.png 因此也需要加上模块名

this.$store.commit('a/increment', 2)

...mapMutations({
  increment: 'a/increment'
})

actions和mapActions actions和mutations一样,从下面打印信息可以看出来, actions 因此也需要加上模块名

企业微信截图_bee25d62-189b-400f-b696-1ae44598bcbc.png

this.$store.dispatch('a/increment', 2)

...mapActions({
  increment: 'a/increment'
})

从项目目录开始模块化

虽然上面那种方式确实实现了模块化开发,但是所有代码还是放在了同一个文件中,从项目目录开始模块化实际上是第一个的深度优化,在开发过程中我们使用这种方式也要更多一些。

将模块的代码拆成独立的文件:a.js和b.js,把这些文件放在module文件夹里面,目录如下:

企业微信截图_657a7657-738f-4ee0-afbe-7e5231a15980.png

a.js

文件内容如下:

export default {
  namespaced: true,
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    odds: state => {
      return state.d.filter(item => item % 2 !== 0)
    }
  },
  mutations: {
    increment (state, value) {
      state.a += value
    }
  },
  actions: {
    increment (context, value) {
      context.commit('increment', value)
    }
  }
}

b.js

文件内容如下:

export default {
  namespaced: true,
  state: {
    a: 1,
    b: 2,
    c: 3,
    d: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  },
  getters: {
    odds: state => {
      return state.d.filter(item => item % 2 === 0)
    }
  },
  mutations: {
    increment (state, value) {
      state.a += value
    }
  },
  actions: {
    increment (context, value) {
      context.commit('increment', value)
    }
  }
}

index.js

文件内容如下

import Vue from 'vue'
import Vuex from 'vuex'
import a from './modules/a'
import b from './modules/b'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    a,
    b
  }
})