在Vuex中,同时拥有“动作”和“突变”的逻辑是什么?
我理解组件不能修改状态的逻辑(这看起来很聪明),但同时拥有动作和突变似乎是在编写一个函数来触发另一个函数,然后再改变状态。
“动作”和“突变”之间的区别是什么,它们是如何一起工作的,更重要的是,我很好奇Vuex开发人员为什么决定这样做?
在Vuex中,同时拥有“动作”和“突变”的逻辑是什么?
我理解组件不能修改状态的逻辑(这看起来很聪明),但同时拥有动作和突变似乎是在编写一个函数来触发另一个函数,然后再改变状态。
“动作”和“突变”之间的区别是什么,它们是如何一起工作的,更重要的是,我很好奇Vuex开发人员为什么决定这样做?
当前回答
根据文件
动作类似于突变,区别在于:
操作不会导致状态突变,而是导致突变。 动作可以包含任意异步操作。
考虑下面的代码片段。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++ //Mutating the state. Must be synchronous
}
},
actions: {
increment (context) {
context.commit('increment') //Committing the mutations. Can be asynchronous.
}
}
})
动作处理程序(增量)接收一个上下文对象,该对象公开相同的 方法/属性,以便您可以调用 commit来提交一个突变,或者访问状态和getter 通过上下文。State和context.getter
其他回答
我认为TLDR的答案是,突变意味着同步/事务性的。因此,如果您需要运行Ajax调用,或执行任何其他异步代码,则需要在Action中执行该操作,然后提交一个突变,以设置新状态。
这也让我感到困惑,所以我做了一个简单的演示。
component.vue
<template>
<div id="app">
<h6>Logging with Action vs Mutation</h6>
<p>{{count}}</p>
<p>
<button @click="mutateCountWithAsyncDelay()">Mutate Count directly with delay</button>
</p>
<p>
<button @click="updateCountViaAsyncAction()">Update Count via action, but with delay</button>
</p>
<p>Note that when the mutation handles the asynchronous action, the "log" in console is broken.</p>
<p>When mutations are separated to only update data while the action handles the asynchronous business
logic, the log works the log works</p>
</div>
</template>
<script>
export default {
name: 'app',
methods: {
//WRONG
mutateCountWithAsyncDelay(){
this.$store.commit('mutateCountWithAsyncDelay');
},
//RIGHT
updateCountViaAsyncAction(){
this.$store.dispatch('updateCountAsync')
}
},
computed: {
count: function(){
return this.$store.state.count;
},
}
}
</script>
store.js
import 'es6-promise/auto'
import Vuex from 'vuex'
import Vue from 'vue';
Vue.use(Vuex);
const myStore = new Vuex.Store({
state: {
count: 0,
},
mutations: {
//The WRONG way
mutateCountWithAsyncDelay (state) {
var log1;
var log2;
//Capture Before Value
log1 = state.count;
//Simulate delay from a fetch or something
setTimeout(() => {
state.count++
}, 1000);
//Capture After Value
log2 = state.count;
//Async in mutation screws up the log
console.log(`Starting Count: ${log1}`); //NRHG
console.log(`Ending Count: ${log2}`); //NRHG
},
//The RIGHT way
mutateCount (state) {
var log1;
var log2;
//Capture Before Value
log1 = state.count;
//Mutation does nothing but update data
state.count++;
//Capture After Value
log2 = state.count;
//Changes logged correctly
console.log(`Starting Count: ${log1}`); //NRHG
console.log(`Ending Count: ${log2}`); //NRHG
}
},
actions: {
//This action performs its async work then commits the RIGHT mutation
updateCountAsync(context){
setTimeout(() => {
context.commit('mutateCount');
}, 1000);
}
},
});
export default myStore;
经过研究,我得出的结论是,突变是一种只关注于更改数据以更好地分离关注点和改进更新数据前后的日志记录的约定。而动作是一个抽象层,它处理更高层次的逻辑,然后适当地调用突变
这似乎没有必要有一个额外的操作层来调用突变,例如:
const actions = {
logout: ({ commit }) => {
commit("setToken", null);
}
};
const mutations = {
setToken: (state, token) => {
state.token = token;
}
};
因此,如果调用actions调用注销,为什么不调用突变本身呢?
动作的整个思想是从一个动作内部调用多个突变,或者发出Ajax请求或任何你能想到的异步逻辑。
我们最终可能会有发出多个网络请求的动作,并最终调用许多不同的突变。
因此,我们尝试将尽可能多的复杂性从我们的Vuex.Store()中添加到我们的动作中,这使得我们的突变、状态和getter更干净、更直接,并与使Vue和React等库流行的模块化保持一致。
因为没有突变就没有状态!提交时——执行以可预见的方式改变状态的一段逻辑。突变是设置或改变状态的唯一方法(所以没有直接的变化!),而且它们必须是同步的。这个解决方案驱动了一个非常重要的功能:突变将登录到devtools。这为您提供了良好的可读性和可预测性!
还有一件事——行动。正如我们所说的,行为会导致突变。所以它们不会改变存储,也不需要这些是同步的。但是,他们可以管理一个额外的异步逻辑!
突变是同步的,而操作可以是异步的。
换句话说:如果您的操作是同步的,则不需要操作,否则就实现它们。