vuex为何不建议在action中修改state

背景

在最近的一次需求开发过程当中,有再次使用到Vuex,在状态更新这一方面,我始终遵循着官方的“叮嘱”,谨记“必定不要在action中修改state,而是要在mutation中修改”;因而我不由产生了一个疑问:Vuex为何要给出这个限制,它是基于什么缘由呢?带着这个疑问我查看Vuex的源码,下面请你们跟着个人脚步,来一块儿揭开这个问题的面纱。javascript

一块儿阅读源码吧~

1.首先咱们能够在src/store.js这个文件的Store类中找到下面这段代码前端

// ...
this.dispatch = function boundDispatch (type, payload) {
  return dispatch.call(store, type, payload)
}
this.commit = function boundCommit (type, payload, options) {
  return commit.call(store, type, payload, options)
}
// ...
复制代码

上面是Vuex两个最核心的API:dispatch & commit,它们是分别用来提交action和mutation的 那么既然咱们今天的目的是为了“了解为何不能在action中修改state”,因此咱们就先看看mutation是怎样修改state的,然而mutation是经过commit提交的,因此咱们先看一下commit的内部实现vue

commit&mutation

2.commit方法的核心代码大体以下:java

commit (_type, _payload, _options) {
    // ...
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })
    // ...
}
复制代码

不难看出,Vuex在commit(提交)某种类型的mutation时,会先用_withCommit包裹一下这些mutation,即做为参数传入_withCommit;那么咱们来看看_withCommit的内部实现(ps:这里之因此说”某种类型的mutation“,是由于Vuex的确支持声明多个同名的mutation,不过前提是它们在不一样的namespace下;action同理)vuex

3._withCommit方法的代码以下:并发

_withCommit (fn) {
    const committing = this._committing
    this._committing = true
    fn()
    this._committing = committing
  }
复制代码

是的,你没有看错,它真的只有4行代码;这里咱们注意到有一个标志位_committing,在执行fn前,这个标志位会被置为true,这个点咱们先记下,一下子会用到异步

4.接下来,我要为你们要介绍的是resetStoreVM这个函数,它的做用是初始化store,它首次被执行是在Store的构造函数中ide

function resetStoreVM (store, state, hot) {
  // ...
  if (store.strict) {
    enableStrictMode(store)
  }
// ...
}
复制代码

在这里有一处须要咱们注意:resetStoreVM对strict(是否启用严格模式)作了判断,这里假设咱们启用严格模式,那么就会执行enableStrictMode这个函数,下面继续来看看它的内部实现函数

function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    if (process.env.NODE_ENV !== 'production') {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}
复制代码

这里对Vue组件实例的state作了监听,一旦监听到变化,就会执行asset(断言),它断言的恰巧就是刚才我让你们记住的那个_committing标志位,那么咱们再来看看这个asset作了些什么性能

5.asset方法在src/util.js这个文件中

export function assert (condition, msg) {
  if (!condition) throw new Error(`[vuex] ${msg}`)
}
复制代码

这个方法很简单,就是判断第一个参数是否为truly值,若是不为真,就抛出一个异常

到此,咱们已简单地了解了commit和mutation的逻辑,下面再来看看dispatch和action

dispatch&action

6.dispatch代码大体以下:

dispatch (_type, _payload) {
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)

    const action = { type, payload }
    const entry = this._actions[type]

  // ...
    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
  // ...
  }
复制代码

这里咱们注意到,当某种类型的action只有一个声明时,action的回调会被看成普通函数执行,而当若是有多个声明时,它们是被视为Promise实例,而且用Promise.all执行,总所周知,Promise.all在执行Promise时是不保证顺序的,也就是说,假若有3个Promise实例:P一、P二、P3,它们3个之中不必定哪一个先有返回结果,那么咱们仔细思考一下:若是同时在多个action中修改了同一个state,那会有什么样的结果?

其实很简单,咱们在多个action中修改同一个state,由于颇有可能每一个action赋给state的新值都有所不一样,而且不能保证最后一个有返回结果action是哪个action,因此最后赋予state的值多是错误的

那么Vuex为何要使用Promise.all执行action呢?其实也是出于性能考虑,这样咱们就能够最大限度进行异步操做并发

眼尖的同窗可能已经发如今dispatch中并无看到_committing的身影,就是Vuex对action修改state的限制:当action想要修改state时,由于_committing没有事先被置为true,而致使asset阶段没法经过

但这个限制只限于开发阶段,由于在enableStrictMode函数中,Webpack加入了对环境的判断,若是不是生产环境(也就是开发环境)才会输出asset(断言)这行代码

function enableStrictMode (store) {
  store._vm.$watch(function () { return this._data.$$state }, () => {
    if (process.env.NODE_ENV !== 'production') {
      assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
    }
  }, { deep: true, sync: true })
}
复制代码

那么也就是说若是你强行在生产环境中用action修改state,Vuex也不会阻止你,它可能仅仅是给你一个警告;并且按道理来讲,若是咱们可以保证同一类型的action只有一个声明,那么不管是使用action仍是mutation来修改state结果都是同样的,由于Vuex针对这种状况,没有使用Promise.all执行action,因此也就不会存在返回结果前后问题

dispatch (_type, _payload) {
    // ...
    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
    // ...
  }
复制代码

可是凡是靠人遵照的约定都是不靠谱的,因此咱们在平时使用Vuex时,最好仍是遵照官方的约束,不然线上代码有可能出现bug,这不是咱们所指望的。

结束语

Vuex这一限制其实也是出于代码设计考虑,action和mutation各司其事,本质上也是遵照了“单一职责”原则。以上就是我对“Vuex为何不容许在action中修改状态“这个问题的分析,但愿对你们有所帮助,也欢迎指正

关注咱们

关注公众号前端论道
相关文章
相关标签/搜索