第 37 题:为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作

发布于 2022-10-29 10:18:16 字数 78 浏览 131 评论 13

vuex 是一个专为 vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

屌丝范 2022-05-04 13:48:40

可以是可以,只是不推荐这么做,否则在vue的dev-tools中,无法方便的追踪一个数据的上下文,因为不知道哪个修改在前,哪个修改在后,时间线会乱掉

似最初 2022-05-04 13:43:02

首先明确一点就是:
mutaition, reducer其实就是个函数,没有明确规定不能做异步。江湖百态,并不全是武当派浩气长存,也有星宿派法力无边。看你怎么用了。
官方只是规定,或者说强行建议你在这里面做同步操作,将它作为一个无副作用的函数,使得你的状态可预测的,方便维护。
总的来说,这是一种规范。但在这里面做异步程序也不会报错。
反正我是武当派。

双手揣兜 2022-05-04 13:37:52

单一职责原则:mutation 或 reducer 只作为数据的输入输出, 即 state 的变更接口,纯函数。这样做的好处就是可以更好解耦和接口封装。

瑾夏年华 2022-05-04 13:21:58

vuex中在mutation中使用异步,其实对结果是没有影响的
只是人为规定不能在mutation中使用异步

  1. 为了devtool快照可以正确记录值的变化
  2. 设计理念,将有副作用的函数放在action中,同步修改放在mutation中
恍梦境° 2022-05-04 12:46:51

vuex 的mutaion操作通过commit进行触发,
在commit方法的内部通过_withComit方法对state数据进行修改
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
}),
同时内部维护了一个this._subscribers 的订阅中心,当有mutation被执行的时候,commit会触发this._subscribers 中的函数
if (!options || !options.silent) {
this._subscribers.forEach(sub => sub(mutation, this.state))
}
那么在进行异步操作的时候,就不会准备的捕捉到修改后的state的状态,从而导致依赖发布订阅模式实现的logger 或者 devtools都不能准备的捕捉状态

下面的描述来自于vuex的文档
现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

森林很绿却致人迷途 2022-05-04 12:34:52

中文翻译可能有些偏差(不是我翻的)。区分 actions 和 mutations 并不是为了解决竞态问题,而是为了能用 devtools 追踪状态变化。

事实上在 vuex 里面 actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发 mutation 就行。异步竞态怎么处理那是用户自己的事情。vuex 真正限制你的只有 mutation 必须是同步的这一点(在 redux 里面就好像 reducer 必须同步返回下一个状态一样)。

同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态(和 reducer 一样),这样 devtools 就可以打个 snapshot 存下来,然后就可以随便 time-travel 了。如果你开着 devtool 调用一个异步的 action,你可以清楚地看到它所调用的 mutation 是何时被记录下来的,并且可以立刻查看它们对应的状态。其实我有个点子一直没时间做,那就是把记录下来的 mutations 做成类似 rx-marble 那样的时间线图,对于理解应用的异步状态变化很有帮助。

#照搬过来,给更多人看到

作者:尤雨溪
链接:https://www.zhihu.com/question/48759748/answer/112823337
来源:知乎

歌枕肩 2022-05-04 11:40:05

Mutation 必须是同步函数
一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。

在组件中提交 Mutation
你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

参考:Mutation 必须是同步函数

辞慾 2022-05-04 08:38:12

因为更改state的函数必须是纯函数,纯函数既是统一输入就会统一输出,没有任何副作用;如果是异步则会引入额外的副作用,导致更改后的state不可预测;

我喜欢麦丽素 2022-05-04 08:32:02

因为异步操作是成功还是失败不可预测,什么时候进行异步操作也不可预测;当异步操作成功或失败时,如果不 commit(mutation) 或者 dispatch(action),Vuex 和 Redux 就不能捕获到异步的结果从而进行相应的操作

瀞厅☆埖开 2022-05-03 21:05:13

vue用的不是很多,所以不是很清楚mutation里面为什么不能有异步操作,下面解释一下为什么Redux的reducer里不能有异步操作。

  1. 先从Redux的设计层面来解释为什么Reducer必须是纯函数

如果你经常用React+Redux开发,那么就应该了解Redux的设计初衷。Redux的设计参考了Flux的模式,作者希望以此来实现时间旅行,保存应用的历史状态,实现应用状态的可预测。所以整个Redux都是函数式编程的范式,要求reducer是纯函数也是自然而然的事情,使用纯函数才能保证相同的输入得到相同的输入,保证状态的可预测。所以Redux有三大原则:

  • 单一数据源,也就是state
  • state 是只读,Redux并没有暴露出直接修改state的接口,必须通过action来触发修改
  • 使用纯函数来修改state,reducer必须是纯函数
  1. 下面在从代码层面来解释为什么reducer必须是纯函数

那么reducer到底干了件什么事,在Redux的源码中只用了一行来表示:

currentState = currentReducer(currentState, action)

这一行简单粗暴的在代码层面解释了为什么currentReducer必须是纯函数。currentReducer就是我们在createStore中传入的reducer(至于为什么会加个current有兴趣的可以自己去看源码),reducer是用来计算state的,所以它的返回值必须是state,也就是我们整个应用的状态,而不能是promise之类的。

要在reducer中加入异步的操作,如果你只是单纯想执行异步操作,不会等待异步的返回,那么在reducer中执行的意义是什么。如果想把异步操作的结果反应在state中,首先整个应用的状态将变的不可预测,违背Redux的设计原则,其次,此时的currentState将会是promise之类而不是我们想要的应用状态,根本是行不通的。

其实这个问题应该是Redux中为什么不能有副作用的操作更合适。

薔薇婲 2022-05-03 20:20:24

应该是各自对于状态管理机制的一种涉及。vue和redux都是一种状态管理机制。 然后他们会有自己的state、和修改state的方法, 修改state的方法涉及到同步和异步,vuex的处理方式是同步在mutation里面,异步在actions里面,然后redux的同步就是reducer ,异步更多的是用户自己去通过中间件的方式去实现的把。 没写过redux 我只能理解到这里了。

坏尐絯 2022-05-03 17:58:59

Vuex的异步操作可以放在action里面,用Promise或者async,await都是不错的选择

~没有更多了~

关于作者

嗫嚅

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

烙印

文章 0 评论 0

singlesman

文章 0 评论 0

独孤求败

文章 0 评论 0

晨钟暮鼓

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文