vue2底层数据监测原理
1. 变化侦测
底层是基于Object.defineProperty(data,key,option)来实现的
对每一个需要侦测的数据需要执行如下操作
【只能侦测数据中某一个属性】
此时存在俩种情况 1 属性为基本数据类型 2属性为对象 为了满足把数据所有属性【包括子属性都侦测到】
做了一个递归调用上图方法的操作 对侦测的属性 执行 new Observer(obj)的操作 【下图的walk函数】
Watcher的创建 :
作用 充当一个中间者 数据变化的时候通知watcher 然后它再通知其他地方(调用回调函数)
watcher的getter函数为啥执行就可以读取所要属性的值呢? 在parsePath函数中 进行了路径的解析 逐层读取 最后返回所需要的值
所以此时我们执行watcher实例的getter方法相当于读取了该属性 由于读取的属性之前已经被转化为getter/setter形式 此刻会执行数据的getter 在其中会进行一个收集依赖的操作【收集watcher,相当于这个watcher监听了这个属性的变化】
依赖列表
为了保存这个依赖 创建了一个依赖列表Dep 在被侦听数据的getter阶段 执行了这个收集的动作
**如何被收集呢**
在getter会获取一个全局的变量 比如为window.target 此时该变量的值为需要收集的依赖
然后在这之前 在watcher当中 会主动
一般数据更新
在被侦听数据更新的时候 在setter阶段 会通知这个数据的依赖列表中所以已经收集的依赖的update方法 ,可以看Watcher的介绍
然后Watcher就能监听到这个数据的变化并且 通过回调函数告知给我们
针对数组更新
1 自身指针发送变动 类似一般数据更新 触发setter
2 针对于元素的增删 在new Observer的时候 针对数组做了特殊的处理【重写7个改变数组本身的方法 】
处理方法:不覆盖全局的数组的原型 针对侦测数据 如果有__proto__指针 就将重写的覆盖器设置为侦测数组的原型 如果没有__proto__ 强制转化数组的改变自身的方法(本身没有的话才回去原型上读取)
重写做的处理:1在调用方法后 会主动触发已收集依赖的更新动作【依赖也是在getter中收集的】 2 将传入的元素收集 然后转化为getter/setter类型【三种情况 基本数据类型 普通对象 数组】
之前我们说过被侦测的数据都会进行new Observer的操作 此时创建的observer实例会被添加到被侦测的数据上 属性名为__ob__ 为了方便重写数组的方法能够拿到 【数组方法调用 会进行this.ob.dep.notice() 通知已收集的依赖】
$watch 和watch的实现
我们知道vm.$watch()可以主动监听某一个属性的变化 一般使用的时候 第一个参数传的字符串 回去读取该路径的数据 其实第一个参数也可传一个函数 此时会替代Watcher内部的getter方法 并且此时该函数中使用到的数据都会收集此时创建的Watcher作为依赖【一watcher对多个dep依赖列表】
watch的本质其实就是创建了一个watcher实例 并且被需要侦测的数据的依赖列表所收集
当deep为true的时候 会让侦测的数据的所有的子集去收集一下此时所创将的watcher到自己的依赖列表
$set的实现
会进行判断 1 如果操作的对象不是响应式的(没有__ob__ 就直接进行属性的增加) 2 如果操作的为数组的话 就直接调这个数组的splice方法 【内部会通知依赖】 3 如果操作的是响应式对象的话 已有属性就正常替换 如果是新增属性的话 会将这个新增的属性变为getter/setter形式【进行defineReactive】 然后会通知被操作数据的依赖去进行更新
$delete实现
数组【splice方法】删除之后 通过delete关键字【对象】会通知被操作数据的依赖去进行更新(this.ob.dep.notify() )