Object.defineProperty()
用于数据劫持和数据代理,能够检测对象属性的变化
Object.defineProperty(obj,'key',configOption) //新增或者修改属性
configOption = {
value,
enumerable,
writable,
get,
set,
...
}
使用defineObject定义的getter setter需要使用外部变量周转才能使用
- setter 设置 temp
- getter 返回 temp
defineReactive
将temp封装到内部,通过闭包的方式在defineReactive保存了Object.defineProperty的temp临时变量
functino defineReactive(data,key,val) {
Object.defineProperty(data,key,{
enumberabel:true,
configurabel:true,
get(){
return val
},
set(newValue){
val = newValue
}
}
}
Observer
通过递归生成深层次响应式内容
数组的响应式
Observer
数组不用去执行 defineProperty 方法,他的循环从 Observer实例 到 observe 在 Observer 实例中改变原型,调用observer,在新原型方法上再次触发 observe 方法,将新增的内容变为响应式内容
改写7个数组方法
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
改写 Array.prototype 上的7个操作方法
其中push , unshift , splice 这些需要推入新数据的需要保证新数据也是 observed 的,从 ob 上获取observer实例,调用其 ArrayWalk 遍历方法
以Array.prototype为原型创建了一个arrayMethods,
将数组的原型指向 arrayMethods,调用改写后的7个方法
-
ArrayMethods = Obejct.create(Array.prototype)
-
改写ArrayMethods上7个方法
-
修改数组原型
- o.proto = arrayMethods 或者
- Obejct.setPrototypeOf(o,arrayMethods)
methodNeedChange.forEach(methodName => {
const original = arrayPrototype[methodName]
def(arrayMethods,methodName,function() {
const args = [...arguments]
// 将新增的内容也变成响应式的
switch (methodName):
{
case 'push':
case 'unshift':
inserted = args
case 'splice':
inserted = args .slice(2)
break;
}
if(inserted){
this.__ob__.observerArray(inserted)
}
return original.apply(this,args )
// 不能使用箭头函数,因为我们需要this和arguments
}
})
原因
数组也可以视作对象,使用Object.defineProperty也能监听数组元素的修改,但无法监听数组方法操作的,所以vue干脆直接放弃了使用Object.defineProperty方法
依赖收集
- 在getter中收集依赖,收集的是watcher
- 在setter中触发依赖,触发的也是watcher
- 每一个Observer实例中有一个Deps实例
Watcher 可以理解为 vue3 中的 effect
Dep Class
拥有闭包全局唯一id
在哪里实例化
-
Dep 类在 Observer 的实例中被实例化 ,每一个对象都会有一个Observer实例,所以就有一个Dep实例。用于保存当前响应式对象的Watcher(effect 依赖)
-
在defineReactive 的时候实例化Dep,这个是最后一层非对象的响应式数据的Dep, 是在defineReactive的闭包中的
Dep.depend 添加收集
通过全局变量 Dep.target (全局的)判断当前触发getter的函数,从Vue3的角度看回来,这个就是activeEffect的意思
将Dep.target(watcher实例)添加到Dep中
-
由响应式数据的getter触发
-
defineReactive中定义,调用Dep的depend方法
-
判断是否有Watcher,将Watcher 添加到Dep的订阅者列表中
Dep.notify 触发依赖
Dep中获取到Watcher的实例列表
-
由数据的setter触发
-
defineReactive中定义调用 Dep 的 notify 方法
-
通知所有watcher,执行回调函数,如果是render watcher 则触发更新流程
Watch Class
闭包全局唯一id
依赖收集
组件渲染或者计算属性的时候会创建Watcher实例(Effect),在渲染的时候将本Watcher实例设置Dep.target(全局),从而触发依赖手机
更新操作
Watcher 的update函数相当于Vue3的effect,当响应式内容变化时,依赖被触发,watcher的update被执行,重新计算或者渲染视图
笔者才疏学浅,各位读者多多担待,不吝赐教。部分插图来自网络,侵删。