使用Vuex的好处
- 能够在Vuex中集中管理共享数据,易于开发和后期维护
- 能够高效地实现组件之间的数据共享,提高开发效率
- 在Vuex中的数据都是响应式的
Vuex的状态管理模式
为什么要用到Vuex Modules
Vuex Modules简单使用
我们在src目录下建立我们的store文件夹,用来写一些vuex module的简单使用。
1.index.js
// index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { moduleA } from './moduleA'
import { moduleB } from './moduleB'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
name: 'index'
},
mutations: {
increment(state, payload) {
state.count += payload.amount;
console.log("mutations" + state.name)
},
},
actions: {
incrementAsync({ state, commit }, products) {
setTimeout(() => {
console.log("actions" + state.name)
commit('increment', products)
}, 1000)
}
},
modules: { moduleA, moduleB },
getters: {
getCount: (state) => {
return "store被调用了" + state.count + "次"
}
}
})
2.moduleA.js
// moduleA.js
export const moduleA = {
state: {
nameA: "moduleA",
countA: 0
},
mutations: {
incrementA(state, payload) {
state.countA += payload.amount;
console.log("mutations" + state.nameA)
}
},
actions: {
incrementAsyncA({ commit }, products) {
setTimeout(() => {
commit('incrementA', products)
console.log("actions" + state.nameA)
}, 1000)
}
},
getters: {
getCountA: (state, getters) => {
console.log("getter" + state.nameA)
return state.nameA + "被调用了" + state.countA + "次"
}
}
}
3.moduleB.js
// moduleB.js
export const moduleB = {
state: {
nameB: "moduleB",
countB: 0
},
mutations: {
incrementB(state, payload) {
state.countB += payload.amount;
console.log("mutations" + state.nameB)
}
},
actions: {
incrementAsyncB({ commit }, products) {
setTimeout(() => {
commit('incrementB', products)
console.log("actions" + state.nameB)
}, 1000)
}
},
getters: {
getCountB: (state, getters) => {
console.log("getter" + state.nameB)
return state.nameB + "被调用了" + state.countB + "次"
}
}
}
4.页面
<template>
<div class="hello">
<div>
{{name}}
<button @click="incrementFun">{{count}}</button>
{{getCount}}
</div>
<div>
{{nameA}}
<button @click="incrementFunA">{{countA}}</button>
{{getCountA}}
</div>
<div>
{{nameB}}
<button @click="incrementFunB">{{countB}}</button>
{{getCountB}}
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed:{
...mapState({
count: state => state.count,
name: state => state.name,
countA: state => state.moduleA.countA,
nameA: state => state.moduleA.nameA,
countB: state => state.moduleB.countB,
nameB: state => state.moduleB.nameB
}),
...mapGetters([
'getCount',
'getCountA',
'getCountB'
])
},methods:{
...mapMutations([
'increment',
'incrementA',
'incrementB'
]),
...mapActions([
'incrementAsync',
'incrementAsyncB',
'incrementAsyncB'
]),
incrementFun(){
this.increment({amount:1})
console.log(this.$store.state);
},
incrementFunA(){
this.incrementA({amount:1})
},
incrementFunB(){
this.incrementB({amount:1})
}
}
}
</script>
<style scoped>
</style>
我们需要注意的是,不同mutation以及action中函数的名字相同会出现函数被重复调用的情况。
模块的局部状态
对于模块内部的mutation和getter,接收到的第一个参数是模块的局部状态对象。
**在getter中,根节点状态会作为第三个参数暴露出来。
//moduleA.js
mutations: {
incrementA(state, payload) {
state.countA += payload.amount;
console.log(state);
}
},
getters: {
getCountA: (state, getters, rootState) => {
console.log(state);
console.log(rootState);
return state.nameA + "被调用了" + state.countA + "次"
}
}
1.根据输出我们可以看到,这里的state只是moduleA的state对象。
2.同样,对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootState
// moduleA.js
actions: {
incrementAsyncA({ commit, state, rootState }, products) {
setTimeout(() => {
console.log(state);
console.log(rootState);
commit('incrementA', products)
}, 1000)
}
}
命名空间
默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的,这样使得多个模块能够对同一mutation或action做出响应。
如果我们想让我们的模块具有更高的封装度和复用性,可以通过添加namespaced: true的方式使其成为带命名空间的模块。当模块被注册后,他的所有getter、action及matation都会自动根据模块注册的路径调整命名。
// index.js 只倒入moduleA
modules: { moduleA }
// moduleA
import { moduleB } from './moduleB'
import { moduleC } from './moduleC'
//moduleA.js
export const moduleA = {
namespaced: true,
state: {
nameA: "moduleA",
countA: 0
},
mutations: {
incrementA(state, payload) {
state.countA += payload.amount;
}
},
actions: {
incrementAsyncA({ commit, state, rootState }, products) {
setTimeout(() => {
commit('incrementA', products)
}, 1000)
}
},
getters: {
getCountA: (state, getters, rootState) => {
return state.nameA + "被调用了" + state.countA + "次"
}
},
//嵌套子模块
modules: {
moduleB,
moduleC
}
}
// moduleB.js moduleB没有设置namespaced: true会继承父模块命名空间
export const moduleB = {
state: {
nameB: "moduleB",
countB: 0
},
mutations: {
incrementB(state, payload) {
state.countB += payload.amount;
}
},
actions: {
incrementAsyncB({ commit }, products) {
setTimeout(() => {
commit('incrementB', products)
}, 1000)
}
},
getters: {
getCountB: (state, getters) => {
return state.nameB + "被调用了" + state.countB + "次"
}
}
}
//moduleC.js
export const moduleC = {
namespaced: true,
state: {
nameC: "moduleC",
countC: 0
},
mutations: {
incrementC(state, payload) {
state.countC += payload.amount;
}
},
actions: {
incrementAsyncC({ commit }, products) {
setTimeout(() => {
commit('incrementC', products)
}, 1000)
}
},
getters: {
getCountC: (state, getters) => {
return state.nameC + "被调用了" + state.countC + "次"
}
}
}
组件内使用:
<template>
<div class="hello">
<div>
{{name}}
<button @click="incrementFun">{{count}}</button>
{{getCount}}
</div>
<div>
{{nameA}}
<button @click="incrementFunA">{{countA}}</button>
{{getCountA}}
</div>
<div>
{{nameB}}
<button @click="incrementFunB">{{countB}}</button>
{{getCountB}}
</div>
<div>
{{nameC}}
<button @click="incrementFunC">{{countC}}</button>
{{getCountC}}
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
computed:{
...mapState({
count: state => state.count,
name: state => state.name,
countA: state => state.moduleA.countA,
nameA: state => state.moduleA.nameA,
countB: state => state.moduleA.moduleB.countB,
nameB: state => state.moduleA.moduleB.nameB,
countC: state => state.moduleA.moduleC.countC,
nameC: state => state.moduleA.moduleC.nameC
}),
...mapGetters({
getCount: 'getCount',
getCountA: 'moduleA/getCountA',
getCountB: 'moduleA/getCountB',
getCountC: 'moduleA/moduleC/getCountC'
})
},methods:{
...mapMutations({
increment: 'increment',
incrementA: 'moduleA/incrementA',
incrementB: 'moduleA/incrementB',
incrementC: 'moduleA/moduleC/incrementC'
}),
...mapActions({
incrementAsync: 'incrementAsync',
incrementAsyncA: 'moduleA/incrementAsyncA',
incrementAsyncB: 'moduleA/incrementAsyncB',
incrementAsyncC: 'moduleA/moduleC/incrementAsyncC'
}),
incrementFun(){
this.increment({amount:1})
},
incrementFunA(){
this.incrementA({amount:1})
},
incrementFunB(){
this.incrementB({amount:1})
},
incrementFunC(){
this.incrementC({amount:1})
}
}
}
</script>
<style scoped>
</style>
在带命名空间的模块内访问全局内容
// moduleA.js
actions: {
incrementAsyncA({ dispatch, commit, getters, rootGetters }, products) {
setTimeout(() => {
console.log(rootGetters['moduleA/moduleC/getCountC'])
commit('moduleA/incrementB', products, { root: true })
commit('incrementA', products)
}, 1000)
}
},
getters: {
getCountA: (state, getters, rootState, rootGetters) => {
console.log(rootGetters.getCount)
console.log(rootGetters['moduleA/getCountB'])
return state.nameA + "被调用了" + state.countA + "次"
}
}
在带命名空间的模块注册全局action
若需要在命名空间的模块注册全局action,我们可以添加root: true,并将这个action的定义放在函数handler中
actions: {
incrementAsyncA: {
root: true,
handler({ dispatch, commit, getters, rootGetters }, products) {
setTimeout(() => {
commit('incrementA', products)
}, 1000)
}
}
}