天天看点

Vue源码:数据响应式原理

目的

彻底弄懂Vue2 的数据更新原理,手写相关实现代码,让相关知识不再处于“忽悠阶段”

Vue源码:数据响应式原理

从MVVM模式说开去

Vue源码:数据响应式原理

侵入式和非侵入式

Vue源码:数据响应式原理

上帝的钥匙

Object.defineProperty()

数据劫持 / 数据代理

利用JavaScript引擎赋予的功能,检测对象属性变化

仅有"上帝的钥匙"不够,还需要设计一套精密的系统

Object.defineProperty()方法

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

Object.defineProperty()方法接收三个参数,第一个参数是定义哪一个对象,第二个参数是定义什么属性,第三个参数是属性值。

Object.defineProperty()方法可以设置一些额外隐藏的属性

writeable可以定义属性是否可以写

enumerable可以定义属性是否可以被枚举,指的是在调用for in…方法的时候是否会被枚举

getter / setter

属性的getter函数,如果没有getter,则为<code>undefined</code>。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入<code>this</code>对象(由于继承关系,这里的<code>this</code>并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。

默认为<code>undefined</code>。

属性的setter函数,如果没有setter,则为<code>undefined</code>。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的<code>this</code>对象。

这里有一个小坑:用闭包存储get 和set 的值

Vue源码:数据响应式原理

defineReactive函数

getter / setter需要变量周转才能工作

Vue源码:数据响应式原理

使用defineReactive函数不需要设置临时变量,而是用闭包

Vue源码:数据响应式原理

递归侦测对象全部属性

Vue源码:数据响应式原理

数组的响应式处理

改写七个方法,以Array.prototype为原型,创建了一个arrayMethods对象,并且用es6的Object.setPrototypeOf方法,让数组的__proto__指向了arrayMethods,就可以调用新定义的七个函数方法。

Vue源码:数据响应式原理

依赖收集

需要用到数据的地方,称为依赖

Vue1.x,细粒度依赖,用到数据的DOM都是依赖

Vue2.x,中等粒度依赖,用到数组的组件是依赖

在getter中收集依赖,在setter中触发依赖

把依赖收集的代码封装成一个Dep 类,它专门用来管理依赖,每个Observer 的实例,成员中都有一个Dep 的实例;

Watcher 是一个中介,数据发生变化时通过Watcher 中转,通知组件

依赖就是Watcher 。只有Watch触发的getter才会收集依赖,哪个

Watcher 触发了getter,就把哪个Watch收集到Dep中。

Dep 使用发布订阅模式,当数据发生变化时,会循环依赖列表,把所

有的Watcher 都通知一遍。

代码实现的巧妙之处:Watcher把自己设置到全局的一个指定位置,然后读取数据,因为读取了数据,所以会触发这个数据的getter。在getter 中就能得到当前正在读取数据的Watcher,并把这个Watcher 收集到Dep 中。

Vue源码:数据响应式原理

完整代码

Vue源码:数据响应式原理