action
action
类似于 mutation
,不同之处在于:
-
action
提交的是mutation
,而不是直接变更状态。 -
action
可以包含任意异步操作。
可以理解成,为了解决异步更改 state
的问题,需要在 mutation
前添加一层 action
,我们直接操作 action
,然后让 action
去操作 mutation
,如示例代码 6-6-1 所示。
const store = Vuex.createStore({
state: {
count: 3,
},
mutations: {
increment(state,params) {
state.count = state.count + params.num
}
},
actions: {
incrementAction(context, params) {
// 在 action 里面会去调用 mutations
context.commit('increment', params)
}
}
})
const counter = {
template: '<div>{{ count }}<button @click="clb">增加</button></div>',
computed: {
count() {
return this.$store.state.count // 通过 this.$store.state 可以获取 state
}
},
methods: {
clb() {
// 通过 this.$store.dispatch 调用 action
this.$store.dispatch('incrementAction', {
num: 4
})
}
}
}
通过 this.$store.dispatch
可以在 Vue
组件中提交一个 action
,同时可以传递自定义的参数,这和提交一个 mutation
很类似,乍一看感觉多此一举,直接提交 mutation
岂不是更方便?实际上并非如此,还记得 mutation
必须同步执行这个限制吗?action
则不受这个约束。因此可以在 action
内部执行异步操作:
...
incrementAction (context, params) {
setTimeout(()=>{
context.commit('increment',params)
},1000)
}
...
虽然不能在 mutation
执行时进行异步操作,但是可以把异步逻辑放在 action
中,这样对于 mutation
其实是同步的,Chrome 浏览器的 Vue DevTools 也就可以追踪到每一次的状态改变了。
同时,可以在 action
中返回一个 Promise
对象,以便准确地获取异步 action
执行完成后的时间点:
incrementAction (context, params) {
return new Promise((resolve, reject) => {
setTimeout(()=>{
context.commit('increment',params)
},1000)
})
}
...
this.$store.dispatch('incrementAction').then(() => {...})
当然,也可以在一个 action
内部获取当前的 state
或者触发另一个 action
,也可以触发一个 mutation
,代码如下:
actions: {
incrementAction(context) {
if (context.state.count > 1) {
context.dispatch('actionOther')
context.commit('increment1')
context.commit('increment2')
}
},
actionOther() {
console.log('actionOther')
}
}
同样,与 getters
和 mutations
一样,在组件中使用 actions
时,可以用 mapActions
辅助函数来快速在 methods
中映射,代码如下:
...
methods:{
...Vuex.mapActions({
clb:'incrementAction'
})
}
在 Vuex
中,mapActions
是一个帮助函数,用于将 Vuex store
中的 actions
映射到组件的方法中。这使得你可以在组件中更方便地调用 store
的 actions
,从而执行异步操作或复杂的业务逻辑。
为什么使用 mapActions
Actions
在 Vuex 中用于处理异步操作和复杂的业务逻辑。它们可以包含任意异步操作,并且可以通过 commit
提交 mutations
以改变 store
的状态。在组件中使用 mapActions
可以简化调用 actions
的过程,使代码更简洁、更可读。
基本用法
mapActions
可以将 Vuex store
中的 actions
映射为组件的方法。它有两种主要用法:
-
使用字符串数组:直接映射
store
中的actions
。 -
使用对象:重命名方法或创建带参数的方法。
使用字符串数组
假设你的 Vuex store
中有如下 actions
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
increment(context) {
context.commit('increment');
},
incrementAsync(context) {
setTimeout(() => {
context.commit('increment');
}, 1000);
}
}
});
在组件中,你可以使用 mapActions
将这些 actions
映射为组件的方法:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="incrementAsync">Increment Async</button>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['increment', 'incrementAsync'])
}
};
</script>
在这个示例中,increment
和 incrementAsync actions
被映射为组件的方法,可以直接在模板中使用。
使用对象
当你需要重命名方法或传递参数时,可以使用对象形式:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="incrementCount">Increment</button>
<button @click="incrementCountAsync">Increment Async</button>
<button @click="incrementByAmount(5)">Increment by 5</button>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapActions({
incrementCount: 'increment', // 将 `increment` action 映射为 `incrementCount`
incrementCountAsync: 'incrementAsync', // 将 `incrementAsync` action 映射为 `incrementCountAsync`
incrementByAmount({ commit }, amount) {
commit('incrementByAmount', amount);
}
})
}
};
</script>
为了支持 incrementByAmount
方法,需要在 Vuex store
中定义相应的 action
和 mutation
:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
incrementByAmount(state, amount) {
state.count += amount;
}
},
actions: {
increment({ commit }) {
commit('increment');
},
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
},
incrementByAmount({ commit }, amount) {
commit('incrementByAmount', amount);
}
}
});
结合局部方法
如果组件中既需要使用 Vuex store 的 actions
,又需要使用组件的局部方法,可以将它们结合起来:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="incrementAsync">Increment Async</button>
<button @click="localMethod">Local Method</button>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['increment', 'incrementAsync']),
localMethod() {
console.log('This is a local method');
}
}
};
</script>
在这个示例中,组件既有来自 Vuex store 的 actions
方法,也有组件自己的 localMethod
方法。
模块化 Vuex
在使用 Vuex
模块化时,可以指定模块名称来访问嵌套的 actions
:
const store = new Vuex.Store({
modules: {
myModule: {
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
actions: {
increment({ commit }) {
commit('increment');
},
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment');
}, 1000);
}
}
}
}
});
在组件中:
<template>
<div>
<p>Module Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="incrementAsync">Increment Async</button>
</div>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
computed: {
...mapState('myModule', ['count'])
},
methods: {
...mapActions('myModule', ['increment', 'incrementAsync'])
}
};
</script>
在这个示例中,myModule
模块中的 increment
和 incrementAsync actions
被映射为组件的方法。
mapActions
是 Vuex
提供的一个非常方便的工具,可以简化在组件中调用 store actions
的过程。通过字符串数组和对象的两种形式,你可以灵活地映射 store
的 actions
到组件的方法中,并在需要时结合局部方法和模块化的 store
使用。这样,你的代码将更加清晰、简洁,并且易于维护。