Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

学习vuex源码 #3

Open
z2014 opened this issue Jul 19, 2017 · 13 comments
Open

学习vuex源码 #3

z2014 opened this issue Jul 19, 2017 · 13 comments

Comments

@z2014
Copy link
Owner

z2014 commented Jul 19, 2017

昨天听完同学来我们团队做的分享之后,自己又去看了一遍源码,结合自己之前项目的一些理解,写一篇博客,这里是原文链接.

在看源码前,结合之前的自己的项目实践,有以下几个问题:

  • 1.在mutation以外比如vue组件中修改数据,会报错,是怎么做到的
  • 2.我们在一个组件中拿数据的时候要从mapGetters里面映射过来,为什么要存在这个getter呢?

接下来针对上面的问题,结合源码做一下解答:

我们在一个项目中引入vuex是下面的这样一个注入:

Vue.use(Vuex)
export default new Vuex.Store({
  modules: {
    artical,
    editNews,
    login,
    label,
    user
  }
})

其实从上面的这段代码就不难看出,store应该是一个构造函数,对应着vuex源码中store.js
在store.js中有这样的一段代码,简化如下:

class store {
    constructor(){
        this._committing = false;
    }
      _withCommit (fn) {
        const committing = this._committing
        this._committing = true
        fn()
        this._committing = committing
      }
}

这段代码就很好的解释了第一个问题,当我们通过dispatch一个action之后,数据到达mutation里面,我们会将此时的是否编辑状态更改为true,然而当我们直接在组件中修改的时候,此时的_commiting状态依然为false,所以我们往往是做了一层深拷贝之后,才在组件中对数据进行修改。

接下来是第二个问题,为什么要设置一个getter呢?
在我原先的项目中,我的getters里面基本都是这样的函数

const getters = {
  allTableDatas: state => state.tableData,
  allResult: state => state.result
}

其实就是把state中的状态在原封不动的映射出去,当时我在想为什么需要这层映射,直接把state映射出去不好吗,对于一个module里面,如果state里面数据很多的话,那getters也要写很多,岂不是很烦?

但其实看完源码后发现这层getters是必不可少的,为什么呢?

这首先得追溯到vue是如何实现双向绑定的这个问题上,如果不了解的话,可以去看看我的这一篇博客,vue框架本身在你获取一个数据的时候,比如obj.aa,也就是在这个对象的getter里面,就会把你放进一个通知队列里面,当这个对象被重新setter后,会遍历通知队列告诉你我更新了,你也要更新了,并且如果这个数据有watch的话,同时会执行watch的回调函数。

那一个问题就是vuex中的数据是如何实现双向绑定的呢?是和vue一样的方式吗?vuex的源码非常简洁,里面并没有做和vue同样的事情,其实他正是通过了getter方法,与vue的watch相挂钩,才实现了vuex的双向绑定,来看源码中的如下代码

  watch (getter, cb, options) {
    if (process.env.NODE_ENV !== 'production') {
      assert(typeof getter === 'function', `store.watch only accepts a function.`)
    }
    return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
  }

对于一开始的困惑也就解答完了,那一开始说的新大陆是什么呢?

在项目中,当我们对页面上的数据做一些删除或者更新的操作时,我往往会在更新结束之后,在dispatch一个查询操作,就是这个dispatch其实他是可以接收多个action的,而且向每个action传递的参数也一样(主要是之前也没这么用过,读源码的时候发现还能有这种操作,源码如下:

 . dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload)

    const entry = this._actions[type]
    if (!entry) {
      if (process.env.NODE_ENV !== 'production') {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }
    return entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
  }

这篇文章的思路讲的还是比较跳的,没有对源码整体文件目录上做分析就直接讲代码,如果没太看懂的话可以先去看开头的那篇分析,很详细。
最后,欢迎批评指正~

@BigKongfuPanda
Copy link

你好,关于第一个问题:vuex中修改state必须通过commit提交mutation,而不是通过直接修改state。有两个地方没太明白:

  • 1.在commit提交mutation时,会使用_withCommit这个函数,但是其中this._committing = true目的是啥?哪里watch了这个参数是否为true?

  • 2.你在上面提到一句话:......所以我们往往是做了一层深拷贝之后,才在组件中对数据进行修改。.......在commit提交mutation这种方式的时候,哪里进行了深拷贝?难道说commit函数的返回值是一个新的state ?

这些,我在源码中都没有看到,或许是我功力尚浅,请教一下。

@z2014
Copy link
Owner Author

z2014 commented Oct 24, 2017

@BigKongfuPanda 你好,关于第一个问题,你可以把它理解成一个开关,通过commit提交,会将this._committing修改为true,而如果不通过commit提交,则他还会是false,则无法修改。
关于第二个问题,是我之前在做业务时,将state中的数据拿来直接排序,这样会报错,我们需要暴力深拷贝处理之后,再进行组件级的操作

@BigKongfuPanda
Copy link

谢谢回答。但是第一个问题,依然还是存在疑问。_withCommit函数的代码中:
_withCommit (fn) { const committing = this._committing; this._committing = true; fn(); this._committing = committing; }
就算是this._committing == false,下面的回调函数fn依然也会执行啊,这个fn就是修改state的函数吧。
从vuex/store.js中的commit方法里面这段代码可以看得出来:
this._withCommit(() => { entry.forEach(function commitIterator (handler) { handler(payload) }) })

所以,我的意思是,直接修改state,即使不经过_withCommit函数来修改state,此时this._committing == false,那么vuex中有没有对这种情况进行处理呢?

@z2014
Copy link
Owner Author

z2014 commented Oct 24, 2017

@BigKongfuPanda 刚刚在工作。_withCommit这个函数只有在你用commit提交的时候才会调用啊,如果你在外部去修改state,就不会修改this._committing = true,那么在mutation的回调函数中就会报错

@BigKongfuPanda
Copy link

BigKongfuPanda commented Oct 24, 2017

哦。我上面有点说错了。我晓得_withCommit这个函数只有在用commit提交的时候才会调用的。

当外部去修改state,是会报错,但是这个报错的逻辑代码在哪儿呢? 我来回看了几次,都没有找到,您说在mutation的回调函数里面,能够帮我指明不?谢谢。

@BigKongfuPanda
Copy link

我试了下不通过commit提交mutation,直接修改state,控制台并没有报错,而且数据一样是响应式的。

@z2014
Copy link
Owner Author

z2014 commented Oct 25, 2017

'use strict'试试

@BigKongfuPanda
Copy link

BigKongfuPanda commented Oct 25, 2017

刚试了下严格模式下,会报错。

@z2014
Copy link
Owner Author

z2014 commented Oct 25, 2017

demo 有吗,我看看

@BigKongfuPanda
Copy link

第一次,严格模式设置错了。

@BigKongfuPanda
Copy link

我知道了。限制在mutation中去修改state,是为了便于将所有的状态变更都能够被vue的调试工具跟踪到。
我分别试了下两种方式,确实是commit提交mutation的时候,state的变化会被记录下来。

@wkk123
Copy link

wkk123 commented Mar 27, 2018

大美女开始写博客了

@z2014
Copy link
Owner Author

z2014 commented Mar 27, 2018

@wkk123 认错人了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants