我正在使用Vuejs和Vuex开发应用程序。
我有一个名为 settings_operations 的Vuex模块。该模块具有以下作用:
async changePassword ({ commit }, { password, id }) {
commit(CHANGE_PASSWORD_PROCESSING, { id })
const user = auth.currentUser
const [changePasswordError, changePasswordSuccess] = await to(user.updatePassword(password))
if (changePasswordError) {
commit(CHANGE_PASSWORD_ERROR, { id, error: changePasswordError })
} else {
commit(CHANGE_PASSWORD_SUCCESS, changePasswordSuccess)
}
}
编辑:to()为https://github.com/scopsy/await-to-js
具有以下突变:
[CHANGE_PASSWORD_PROCESSING] (state, { id }) {
state.push({
id,
status: 'processing'
})
},
[CHANGE_PASSWORD_ERROR] (state, { id, error }) {
state.push({
id,
error,
status: 'error'
})
}
然后,在组件中,我要使用此状态切片:
computed: {
...mapState({
settings_operations: state => state.settings_operations
})
},
watch: {
settings_operations: {
handler (newSettings, oldSettings) {
console.log(newSettings)
},
deep: false
}
}
问题在于,当changePassword操作导致错误时,手表不会在PROCESSING步骤中停止,而是直接进入ERROR时刻,因此数组将填充2个对象。从字面上看,它跳过了“处理”监视步骤。
发生的一件有趣的事情是,如果我像这样添加setTimeout:
async changePassword ({ commit }, { password, id }) {
commit(CHANGE_PASSWORD_PROCESSING, { id })
setTimeout(async () => {
const user = auth.currentUser
const [changePasswordError, changePasswordSuccess] = await to(user.updatePassword(password))
if (changePasswordError) {
commit(CHANGE_PASSWORD_ERROR, { id, error: changePasswordError })
} else {
commit(CHANGE_PASSWORD_SUCCESS, changePasswordSuccess)
}
}, 500)
},
有效!监视停止两次:第一个刻度显示带有处理对象的数组,第二个刻度显示带有2个对象的数组;处理一个和错误一个。
我在这里想念什么?
编辑:
我在这里重现了这个问题:https://codesandbox.io/s/m40jz26npp
答案 0 :(得分:0)
这是核心团队成员在Vue论坛中给出的答复:
观察者不会在基础数据每次更改时运行。如果它们的观看数据至少更改了一次,则它们仅在下一个“刻度”上运行一次。
您拒绝的try块中的Promise只是一个微任务,它不是 将执行推送到下一个调用堆栈(观察者将在该堆栈上 运行),这样就可以在运行观察程序之前进行错误处理。
另外,当您对一个对象或数组进行突变时,newValue和 深入观察者中的oldValue将是相同的。参见文档:
Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the
相同的对象/数组。 Vue不会保留突变前值的副本。
最后,我从来没有见过有人使用aray作为 模块的根状态,我不知道这是否适用于vuex 所有可能的情况。我当然不建议这样做 这个。
使用同一成员的更好更完整的答案进行编辑:
为什么观察者根本不是异步的?,因为在绝大多数情况下 用例中,观察者只需要对最后的同步更改做出反应 做到了。在大多数情况下(就组件而言), 因为您愿意 即使最终还是重新触发相同的行为多重时间, 只有最后一个状态才是重要状态。
换句话说:默认情况下,对每个更改运行监视程序将 可能会导致应用消耗大量CPU周期而无用 工作。因此观察者是通过异步队列实现的 仅在nexTick上刷新。然后我们不允许重复的观察者 因为观察者的较旧实例将适用于 清空队列后,该状态就不再“存在”。
重要的一点是,这仅适用于同步 更改或在微任务中完成的更改,即立即 解决或失败的诺言-例如,这不会发生 一个ajax请求。
为什么以某种方式实施它们,使得它们在 微任务(即立即解决的诺言? 难以解释,需要一些历史。
最初,在Vue 2.0中,Vue.nextTick被实现为微任务 本身,并且观察者队列在nextTick上刷新。那意味着 那时,一个观察者正在观看被更改为两个的数据 的时间,之间有一个微任务(如承诺),的确会运行 两次。
然后,我认为在2.4左右,我们发现了一个问题 实现,并将Vue.nextTick切换为macroTask。下 此行为,两个更改的数据都将在当前通话中发生 堆栈的microtaks队列,而观察者队列将在 下一个调用堆栈的开始,这意味着它将仅运行一次。
我们发现此实现存在一些新问题, 比起微任务的原始问题要常见得多,所以我们将 可能会切换回2.6中的微任务实现。丑陋的,但是
因此,这应该可以解决问题:
await Vue.nextTick();