数据观察系统是Vue实现数据绑定、异步更新的核心模块,数据观察系统的实现也是Vue源码里最为复杂的部分,在仔细研究具体实现以前,先对整个数据绑定的逻辑架构进行一个充分的认识,会更有助于解读源码。设计模式
先说明一下,由于三个类的名称比较容易让人误解,因此在之后把Observer称做观察目标,Watcher称做监视器,Dep称做依赖对象。数组
Vue的数据观察系统是基于发布者/订阅者模式,数据更新触发刷新页面的过程主要依赖数据观察系统里铁三角关系。在这个系统中,主要角色分别是 Observer
、Dep
、Watcher
这三个对象,对于每个角色在观察数据更新的流程中各自承担的职责须要深刻进行理解。下面请出三个主角登场,来介绍一下它们:架构
Observer
至关于观察目标类,在数据绑定逻辑架构中的职责是收集须要观察的数据对象,进行变量存取器的包装,并递归地对每个须要观察的对象注册发布者对象,再由发布者去注册相应的监视器。这里很是巧妙的是触发通知监视器数据更新的事件的注册,通常的发布订阅模式须要创建一个事件管理器或者调度中心来统一管理各类事件的注册,然而Vue的数据绑定不须要这样的机制,它借用 Object.defineProperty
方法来为每个被监视的数据设置了存取器,依靠数据的存取行为天然地实现了事件的触发。在初始化Vue实例中设置的 data
属性时,对这些输入的数据对象对行了依赖追踪,包装后的变量存放在 _data
属性中,这个过程当中发布者和监视器的依赖添加是不可见的;而经过配置 watch
属性显式设置的监视器,就能够在实例的 _watchers
私有属性中查看到。每一个组件初始化后有一个惟一的 _watcher
对象,它是一个用来监视在 data
中注册的数据变更从而更新视图的监视器,它也默认被添加到了各属性的依赖监视数组中。在每一个修改成可观察状态的属性中,都含有一个 Dep
实例即发布者,这个对象的 subs
属性就是用来存放依赖的全部监视器 Watcher
实例对象,subs
能够理解为订阅者,即全部订阅了该数据对象变更的监视器的数组集合。之因此须要在一开始为数据收集依赖,参考另外一些开发者的总结是因为并不是全部的数据都值得监视,要知道监视没有用到的数据就是对性能的浪费,在实例观察中也确实发现,页面中没有用到的属性,没有被初始化为依赖项,这样即使改变了它的数值,页面也不会触发多余的刷新。异步
Dep
在Vue的数据观察者系统里充当发布者的角色,它不只用来触发数据更新和创建依赖的事件,还用来存放每个可监视数据所依赖的监视器,这个正是在第一步收集依赖时的重要一环。实例初始化的过程当中收集了全部须要跟踪变化的数据,在运用 Observer
从新包装每个属性的同时,建立了各自的 dep
对象,并在get和set方法中分别使用了 Dep
的两个方法:depend
创建依赖,notify
通知变更。另外 Dep
还负责维护依赖监视器的增减。在构造 Dep
类的过程当中,定义了全局的 Dep.target
对象和 targetStack
数组,targetStack
数组是用来存放待执行的 watcher
栈,Dep.target
是用来指代当前的监视器,必须惟一,它的存在对于创建监视器的依赖起到重要做用,在重置数据的 getter
时,当它存在时才执行创建数据与监视器的依赖,即只有显式配置了 watch
或建立了 computed
变量时才会在实例的私有属性里看到监视器。性能
Watcher
是这个架构中的监视器,充当观察者的角色。在Vue实例初始化的过程当中,必定会默认建立一个监视器,这个监视器就是用来监视实例对象的数据变化用来更新视图的,实例的私有属性 _watcher
用来存放它。在建立可观察的数据时,每个数据的 Dep
对象会收集监视器并创建依赖,当数据变化时,Dep
对象通知全部的监视器执行更新,执行更新有两种模式,若是依赖是经过配置 computed
变量建立的,则会当即触发相关的更新操做,若是数据的 dep.subs
数组中没有依赖的监视器,则默认惰性更新模式。Watcher
类最主要的做用是通知视图更新,众所周知视图的更新是很是花费时间,会影响程序性能,为了尽可能减小视图更新致使的性能损失,在通知视图执行更新操做以前会有一个缓冲时段,在这个时段中会收集最后一次监视器收到的变动,减小没必要要的重复更新,实现最优性能。学习
充分了解了数据观察系统的三个主角以后,再来看看官网贴出的示意图,就会发现终于能摸清Vue的数据观察系统的架构了,只不过渲染视图的具体实现与数据观察系统的交互暂时尚未去摸索,之后会仔细地去探索,如今终于比较清晰地弄懂了Vue的数据绑定的原理了。ui
为了更清晰初步了解数据绑定相关的初始化过程,建立了一个很是简单的实例,data配置了两个属性,其中 name
变量并不在页面中使用,还显式设置了一个依赖 msg
的监视器。spa
new Vue({
data () {
return {
msg: 'hello',
name: ''
}
},
watch: {
'msg' (value) {
console.log('msg更新了')
}
}
})
复制代码
下面截图是实例的相关监视器私有属性,_watcher
是跟踪页面渲染的监视器,每一个实例惟一;_wacthers
是实例所拥有的全部监视器的集合。显式设置的 watcher
在是数组中的第一个对象。这里虽然看不到 Observer
背后的包装过程,但改变了 msg
属性以后,能够看到监视器执行的回调显示。.net
从Vue对象实例化着手到开始分析数据绑定的核心实现,这一路过来尚未真正遇到值得困扰的问题。但不曾想到的是,数据绑定这个Vue的核心特点功能居然让我苦苦研读了好几天,彷佛之前对于设计模式的了解显得那样无力。期间去搜索了一些前人作的分析说明文章以求从各个角度深刻理解,但大多数解读读完后依然以为没能很透彻地理解这个模块,后来读到了一个简易实现Vue观察者系统的文章,让我突然对核心逻辑是如何实现的有了比较清晰的认识,并且对于设计模式也有了更深刻的理解。也许第一次读源码的时候太多非核心的技术实现干扰了对于核心部分的理解,也由于以前的一些知识不牢固,因此从这一次学习中获得了一个很好的经验,要更加关注本质。设计