Vuejs观看跳动的跳动

时间:2018-07-13 13:01:28

标签: vue.js vuejs2 vuex vuex-modules

我正在使用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

1 个答案:

答案 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();