详解Vue数据驱动原理
前言
vue区别于传统的js库,例如jquery,其中一个最大的特点就是不用手动去操作dom,只需要对数据进行变更之后,视图也会随之更新。 比如你想修改div#app里的内容:
在代码层面上的最大区别就是,jquery直接对dom进行了操作,而vue则对数据进行了操作,接下来我们通过分析源码来进一步分析,vue是如何做到数据驱动的,而数据驱动主要分成两个部分依赖收集和派发更新。
数据驱动
在vue初始化会执行_init方法,并调用initstate方法. initstate相关代码在src/core/instance/state.js下
我们具体看看initdata是如何定义的。
其中有两个重要的函数分别是proxy跟observe,在往下阅读之前,如果还有不明白object.defineproperty作用的同学,可以点击这里进行了解,依赖收集跟派发更新都需要依靠这个函数进行实现。
proxy
proxy分别传入vm,'_data',data中的key值,定义如下:
proxy函数的逻辑很简单,就是对vm._data上的数据进行代理,vm._data上保存的就是data数据。通过代理的之后我们就可以直接通过this.xxx访问到data上的数据,实际*问的就是this._data.xxx。
observe
oberse定义在src/core/oberse/index.js下,关于数据驱动的文件都存放在src/core/observe这个目录中:
observe函数会对传入的value进行判断,在我们初始化过程会走到new observer(value),其他情况可以看上面的注释。
observer类
observe类要做的事情通过查看源码也是清晰明了,对数据进行响应式处理,并对数组的原型方法进行重写!definereactive函数就是实现依赖收集和派发更新的核心函数了,实现代码如下。
依赖收集
definereactive
代码中多次用到了dep类和dep.target,理解清楚了它们的作用,我们就离vue数据驱动的原理更近一步了,相关的代码如下:
dep
dep的定义还是非常清晰的,代码注释如上,很明显dep跟watcher就跟捆绑销售一样,互相依赖。我们在分析denfinereactive的时候,在对数据进行响应式操作的时候,通过object.defineproperty重写了getter函数。
其中的dep.depend()实际上就是执行了dep.target.adddep(this),this指向dep实例,而dep.target是一个watcher实例,即执行watcher.adddep(this)函数。我们接下来在看看这个函数做了什么:
可以通过下图以便理解data、dep、watcher的关系:
回到代码中,其中dep.addsub(this)就是会把当前的wathcer实例插入到dep.subs的数组中,为之后的派发更新做好准备,这样依赖收集就完成了。但是到现在为止,我们只分析了依赖收集是怎么实现的,但是依赖收集的时机又是在什么时候呢?什么时候会触发getter函数进而实现依赖收集的?在进行依赖收集的时候,dep.tagrget对应wathcer又是什么呢?
watcher大致可以分为三类: * 渲染watcher: 每一个实例对应唯一的一个(有且只有一个) * computed watcher: 每一个实例可以有多个,由computed属性生成的(computed有多少个keyy,实例就有多少个computedwatcher) * user watcher: 每一个实例可以有多个,由watch属性生成的(同computed一样,userwatcher的数量由key数量决定) 为避免混淆,我们接下来说的watcher都是渲染watcher。我们知道在vue初始化的过程中,在执行mountcomponent函数的时候,会执行new watcher(vm, updatecomponent, {}, true),这里的watcher就是渲染watcher
new watcher对于渲染watcher而言,会直接执行this.get()方法,然后执行pushtarget(this),所以当前的dep.target为渲染watcher(用于更新视图)。 而在我们执行this.getter的时候,会调用render函数,此时会读取vm实例上的data数据,这个时候就触发了getter函数了,从而进行了依赖收集,这就是依赖收集的时机,比如
派发更新
我们继续来看definereactive函数里
当我们修改数据的时候,会触发setter函数,这个时候会执行dep.notify,dep.subs中所有的watcher都会执行update方法,对于渲染watcher而言,就是执行this.get()方法,及更新视图。这样一来,就实现了数据驱动。 到这里,vue的数据驱动原理我们就分析完了,如果还对这个流程不大清楚的,可以结合参考官方给的图解:
总结
- 通过object.defineproperty函数改写了数据的getter和setter函数,来实现依赖收集和派发更新。
- 一个key值对应一个dep实例,一个dep实例可以包含多个watcher,一个wathcer也可以包含多个dep。
- dep用于依赖的收集与管理,并通知对应的watcher执行相应的操作。
- 依赖收集的时机是在执行render方法的时候,读取vm上的数据,触发getter函数。而派发更新即在变更数据的时候,触发setter函数,通过dep.notify(),通知到所收集的watcher,执行相应操作。
以上就是详解vue数据驱动原理的详细内容,更多关于vue数据驱动原理的资料请关注其它相关文章!
上一篇: JS中的函数与对象