vue框架的两个抽象核心:虚拟DOM和相应式数据原理vue
关于虚拟DOM的核心算法,咱们上一章已经基本解析过了,详细的见
React && VUE Virtual Dom的Diff算法统一之路 snabbdom.js解读react
关于响应式数据原理,咱们先看张图
你 ‘ (4).png算法
具体来说,要分如下几步:segmentfault
export class Observer { value: any; dep: Dep; vmCount: number; // number of vms that has this object as root $data constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__', this) if (Array.isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) } } /** * Walk through each property and convert them into * getter/setters. This method should only be called when * value type is Object. */ walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } /** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } } }
首先咱们观察到,new Observer()的时候,会进行类型的判断数组
if (Array.isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) }
若是是数组类型则会递归调用,创建依赖体系
不然则调用walk()函数去利用Object.defineProperty简历依赖框架
那么defineReactive是如何实现的呢,以下dom
//省略了部分代码 export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) { const dep = new Dep() ... Object.defineProperty(obj, key, { ... get: function reactiveGetter () { ... dep.depend() ... return value }, set: function reactiveSetter (newVal) { ... dep.notify() } }) }
拦截器会分别在getter和setter上设置中间件去维护dep数组中的依赖关系函数
字面意思很是明显,分别用于增长依赖和通知依赖变化关系ui
class Dep { static target: ?Watcher; id: number; subs: Array<Watcher>; constructor () { this.id = uid++ this.subs = [] } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } }
Dep 就是一个 Watcher 所对应的数据依赖,在这个对象中也存有一个 subs 数组,用来保存和这个依赖有关的 Watcher。其成员函数最主要的是 depend 和 notify ,前者用来设置某个 Watcher 的依赖,后者则用来通知与这个依赖相关的 Watcher 来运行其回调函数。this
咱们知道,Dom 上经过指令或者双大括号绑定的数据,会为数据进行添加观察者watcher,当实例化Watcher的时候 会触发属性的getter方法,此时会调用dep.depend()。咱们看一下depend方法:
depend () { if (Dep.target) { Dep.target.addDep(this) } }
Dep.target 是什么东西呢?其实在进行Watcher实例化的时候,会调用内部的get函数,开始为他初始化
其中pushTarget 方法就是为Dep.target绑定此watcher实例,因此Dep.target.addDep(this)也就是执行此实例中的addDep方法
addDep (dep: Dep) { ... dep.addSub(this) }
这样便为咱们的dep实例添加了一个watcher实例。
接着当咱们更新data的时候,会触发他的set方法,执行dep.notify()函数:
notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } }
这里也就是遍历dep中收集到的watcher实例,进行update()。也就是进行数据更新操做。这也就是简单的数据响应式,其实还须要注意的是: 当数据的getter触发后,会收集依赖,但也不是全部的触发方式都会收集依赖,只有经过watcher触发的getter会收集依赖:if (Dep.target) { dep.depend() },而所谓的被收集的依赖就是当前watcher,DOM中的数据必须经过watcher来绑定,只经过watcher来读取。
最后付一个函数timeline
的.png