Vuex 四大核心辅助函数详解

Vuex 是 Vue.js 应用程序的状态管理模式和库。当我们的应用逐渐复杂,组件之间共享和传递状态变得困难时,Vuex 提供了一个集中的存储来管理所有组件的状态。为了更方便地在组件中使用 Vuex store 中的状态 (state)、取值器 (getters)、变更 (mutations) 和动作 (actions),Vuex 提供了这些辅助函数。它们的主要目的是简化模板代码,减少冗余。


🗺️ mapState

mapState 辅助函数用于帮助我们方便地在组件的计算属性 (computed) 中映射 store 中的 state。

🧩 不用 mapState

当不使用 mapState 时,我们需要为每一个需要从 store 中读取的状态手动创建一个计算属性。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<p>Count: {{ count }}</p>
<p>Message: {{ message }}</p>
<p>User: {{ user.firstName }} {{ user.lastName }}</p>
</div>
</template>

<script>
export default {
computed: {
// 手动定义计算属性来获取 store 中的 state
count() {
return this.$store.state.count; // 直接访问 $store.state
},
message() {
return this.$store.state.message; // 直接访问 $store.state
},
user() {
return this.$store.state.user; // 直接访问 $store.state
}
}
};
</script>

注释说明:

  • computed 对象中,我们为 countmessageuser 分别定义了计算属性。
  • 每个计算属性都返回 this.$store.state 中对应的状态值。

✅ 用了 mapState

使用 mapState 后,代码会变得更加简洁。它可以接收一个数组或一个对象作为参数。

1. 传递字符串数组:

当映射的计算属性的名称与 state 子树的名称相同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<div>
<p>Count: {{ count }}</p>
<p>Message: {{ message }}</p>
<p>Local Computed: {{ localComputed }}</p>
</div>
</template>

<script>
import { mapState } from 'vuex';

export default {
data() {
return {
localData: '这是局部数据'
};
},
computed: {
// 局部计算属性
localComputed() {
return this.localData.toUpperCase();
},
// 使用 mapState 辅助函数将 store 中的 state 映射到局部计算属性
...mapState([
'count', // 映射 this.count 为 store.state.count
'message' // 映射 this.message 为 store.state.message
])
}
};
</script>

注释说明:

  • 我们从 vuex 导入 mapState 函数。
  • 使用对象展开运算符 (...) 将 mapState 返回的对象混入到 computed 对象中。
  • mapState(['count', 'message']) 会生成 { count() { return this.$store.state.count }, message() { return this.$store.state.message } }

2. 传递对象:

当映射的计算属性的名称与 state 子树的名称不同,或者需要更复杂的取值时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>Current Count: {{ currentCount }}</p>
<p>App Message: {{ appMessage }}</p>
<p>Full Name: {{ fullName }}</p>
</div>
</template>

<script>
import { mapState } from 'vuex';

export default {
computed: {
...mapState({
currentCount: 'count', // 将 this.currentCount 映射为 store.state.count
appMessage: state => state.message, // 可以使用函数进行更灵活的映射
fullName: state => `${state.user.firstName} ${state.user.lastName}` // 访问深层嵌套的状态
})
}
};
</script>

注释说明:

  • currentCount: 'count' 表示将组件内的计算属性 currentCount 映射到 Vuex store 中的 state.count
  • appMessage: state => state.messagefullName: state => ... 展示了如何使用函数来获取 state,这提供了更大的灵活性,例如当 state 的值需要计算或来自嵌套对象时。

📊 mapGetters

mapGetters 辅助函数用于将 store 中的 getters 映射到组件的计算属性 (computed) 中。Getters 可以看作是 store 的计算属性。

🧩 不用 mapGetters

不使用 mapGetters 时,你需要为每个 getter 手动创建计算属性。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<p>Doubled Count: {{ doubledCountValue }}</p>
<p>Welcome: {{ welcomeText }}</p>
</div>
</template>

<script>
export default {
computed: {
// 手动定义计算属性来获取 store 中的 getter
doubledCountValue() {
return this.$store.getters.doubledCount; // 通过 $store.getters 访问
},
welcomeText() {
return this.$store.getters.welcomeMessage; // 通过 $store.getters 访问
}
}
};
</script>

注释说明:

  • doubledCountValuewelcomeText 计算属性分别从 this.$store.getters 获取对应的 getter 值。

✅ 用了 mapGetters

使用 mapGetters 可以简化这个过程。它也支持数组和对象两种形式。

1. 传递字符串数组:

当映射的计算属性的名称与 getter 的名称相同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<p>Doubled Count: {{ doubledCount }}</p>
<p>Welcome: {{ welcomeMessage }}</p>
</div>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
computed: {
// 使用 mapGetters 辅助函数将 store 中的 getters 映射到局部计算属性
...mapGetters([
'doubledCount', // 映射 this.doubledCount 为 store.getters.doubledCount
'welcomeMessage' // 映射 this.welcomeMessage 为 store.getters.welcomeMessage
])
}
};
</script>

注释说明:

  • mapGetters(['doubledCount', 'welcomeMessage']) 会生成相应的计算属性,它们的值来源于 Vuex store 中的同名 getters。

2. 传递对象:

当映射的计算属性的名称与 getter 的名称不同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<p>Calculated Double: {{ calcDouble }}</p>
<p>Greeting: {{ greeting }}</p>
</div>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
computed: {
...mapGetters({
calcDouble: 'doubledCount', // 将 this.calcDouble 映射为 store.getters.doubledCount
greeting: 'welcomeMessage' // 将 this.greeting 映射为 store.getters.welcomeMessage
})
}
};
</script>

注释说明:

  • calcDouble: 'doubledCount' 将组件的 calcDouble 计算属性映射到 store 的 doubledCount getter。

🧬 mapMutations

mapMutations 辅助函数用于将 store 中的 mutations 映射到组件的 methods 中。这样你就可以在组件方法中直接调用 this.mutationName(payload) 来提交 mutation,而不是使用 this.$store.commit('mutationName', payload)

🧩 不用 mapMutations

不使用 mapMutations 时,你需要为每个 mutation 手动创建方法来调用 this.$store.commit

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<button @click="incrementCountManually">Increment Count Manually</button>
<button @click="setMessageManually('New Manual Message')">Set Message Manually</button>
</div>
</template>

<script>
export default {
methods: {
// 手动定义方法来提交 mutation
incrementCountManually() {
this.$store.commit('INCREMENT_COUNT', 5); // 手动调用 $store.commit
},
setMessageManually(newMessage) {
this.$store.commit('SET_MESSAGE', newMessage); // 手动调用 $store.commit
}
}
};
</script>

注释说明:

  • incrementCountManually 方法通过 this.$store.commit('INCREMENT_COUNT', 5) 来提交 INCREMENT_COUNT mutation,并传递 payload 5
  • setMessageManually 方法提交 SET_MESSAGE mutation,并传递新的消息字符串。

✅ 用了 mapMutations

使用 mapMutations 可以将 mutations 映射为组件的 methods。

1. 传递字符串数组:

当映射的方法名与 mutation 名相同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<p>Message: {{ $store.state.message }}</p>
<button @click="INCREMENT_COUNT(2)">Increment Count</button> <button @click="SET_MESSAGE('Hello from mapMutations!')">Set Message</button> </div>
</template>

<script>
import { mapMutations } from 'vuex';

export default {
methods: {
// 使用 mapMutations 辅助函数将 store 中的 mutations 映射到局部方法
...mapMutations([
'INCREMENT_COUNT', // 映射 this.INCREMENT_COUNT() 为 this.$store.commit('INCREMENT_COUNT')
'SET_MESSAGE' // 映射 this.SET_MESSAGE() 为 this.$store.commit('SET_MESSAGE')
])
// 调用 this.INCREMENT_COUNT(payload) 等同于 this.$store.commit('INCREMENT_COUNT', payload)
}
};
</script>

注释说明:

  • mapMutations(['INCREMENT_COUNT', 'SET_MESSAGE']) 会将 INCREMENT_COUNTSET_MESSAGE mutations 映射为组件的同名方法。
  • 调用 this.INCREMENT_COUNT(2) 时,2 会作为 payload 传递给 INCREMENT_COUNT mutation。

2. 传递对象:

当映射的方法名与 mutation 名不同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<button @click="addCount(3)">Add to Count</button>
<button @click="changeMessage('Message updated via alias!')">Change App Message</button>
</div>
</template>

<script>
import { mapMutations } from 'vuex';

export default {
methods: {
...mapMutations({
addCount: 'INCREMENT_COUNT', // 将 this.addCount() 映射为 this.$store.commit('INCREMENT_COUNT')
changeMessage: 'SET_MESSAGE' // 将 this.changeMessage() 映射为 this.$store.commit('SET_MESSAGE')
})
// 调用 this.addCount(payload) 等同于 this.$store.commit('INCREMENT_COUNT', payload)
}
};
</script>

注释说明:

  • addCount: 'INCREMENT_COUNT' 将组件的 addCount 方法映射到名为 INCREMENT_COUNT 的 mutation。调用 this.addCount(3) 实际上是执行 this.$store.commit('INCREMENT_COUNT', 3)

🚀 mapActions

mapActions 辅助函数用于将 store 中的 actions 映射到组件的 methods 中。这样你就可以在组件方法中直接调用 this.actionName(payload) 来分发 action,而不是使用 this.$store.dispatch('actionName', payload)

🧩 不用 mapActions

不使用 mapActions 时,你需要为每个 action 手动创建方法来调用 this.$store.dispatch

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<p>Message: {{ $store.state.message }}</p>
<button @click="incrementAsyncManually">Increment Async Manually</button>
<button @click="updateMsgManually('Async new message')">Update Message Async Manually</button>
</div>
</template>

<script>
export default {
methods: {
// 手动定义方法来分发 action
incrementAsyncManually() {
// 手动调用 $store.dispatch,并传递 payload 对象
this.$store.dispatch('incrementAsync', { amount: 10, delay: 500 });
},
updateMsgManually(newMessage) {
// 手动调用 $store.dispatch
this.$store.dispatch('updateMessage', newMessage);
}
}
};
</script>

注释说明:

  • incrementAsyncManually 方法通过 this.$store.dispatch 分发 incrementAsync action,并传递包含 amountdelay 的 payload 对象。
  • updateMsgManually 方法分发 updateMessage action。

✅ 用了 mapActions

使用 mapActions 可以将 actions 映射为组件的 methods。

1. 传递字符串数组:

当映射的方法名与 action 名相同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<p>Message: {{ $store.state.message }}</p>
<button @click="incrementAsync({ amount: 3, delay: 1500 })">Increment Async</button>
<button @click="updateMessage('Hello from mapActions!')">Update Message</button>
</div>
</template>

<script>
import { mapActions } from 'vuex';

export default {
methods: {
// 使用 mapActions 辅助函数将 store 中的 actions 映射到局部方法
...mapActions([
'incrementAsync', // 映射 this.incrementAsync() 为 this.$store.dispatch('incrementAsync')
'updateMessage' // 映射 this.updateMessage() 为 this.$store.dispatch('updateMessage')
])
// 调用 this.incrementAsync(payload) 等同于 this.$store.dispatch('incrementAsync', payload)
}
};
</script>

注释说明:

  • mapActions(['incrementAsync', 'updateMessage'])incrementAsyncupdateMessage actions 映射为组件的同名方法。
  • 调用 this.incrementAsync({ amount: 3, delay: 1500 }) 时,该对象会作为 payload 传递给 incrementAsync action。

2. 传递对象:

当映射的方法名与 action 名不同时。

代码段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<button @click="addAsync({ amount: 7 })">Add to Count Async</button>
<button @click="modifyMessage('Asynchronously changed message!')">Modify App Message Async</button>
</div>
</template>

<script>
import { mapActions } from 'vuex';

export default {
methods: {
...mapActions({
addAsync: 'incrementAsync', // 将 this.addAsync() 映射为 this.$store.dispatch('incrementAsync')
modifyMessage: 'updateMessage' // 将 this.modifyMessage() 映射为 this.$store.dispatch('updateMessage')
})
// 调用 this.addAsync(payload) 等同于 this.$store.dispatch('incrementAsync', payload)
}
};
</script>

注释说明:

  • addAsync: 'incrementAsync' 将组件的 addAsync 方法映射到名为 incrementAsync 的 action。调用 this.addAsync({ amount: 7 }) 实际上是执行 this.$store.dispatch('incrementAsync', { amount: 7 })

总结一下,Vuex 的辅助函数 (mapState, mapGetters, mapActions, mapMutations) 大大简化了在 Vue 组件中与 store 的交互,使得代码更加简洁、易读和易于维护。它们通过将 store 的属性和方法直接映射到组件的 computedmethods 中,减少了模板和脚本中的冗余代码。