Vue Object 的变化侦测实现代码

2020-06-16 06:56:42易采站长站整理

数据观察

Vue 中的对象变化侦测是通过Object.definePorperty实现的,但是Object.definePorperty的方式有缺陷,比如不能直接代理整个对象,每次都要循环遍历对象的所有属性;尤大大说之后会使用 ES6 中的Proxy 重写这个部分。这篇博客介绍的是 Object.definePorperty 实现的对象侦测。

我们来看下面这段代码,定义一个 defineReactive 函数,使用 Object.definePorperty 遍历对象对象属性的时候,设置 get 和 set;当对象属性被读取的时候触发 get,对象属性被设置的时候触发 set。这样就完成了对 data 的数据劫持,因为 Vue 的思想是响应式的,我们还需要收集这些变化。


function defineReactive(data,key,val){
Object.definePorperty(data,key,{
enumerable: true,
configurable: true,
get:function(){
return val;
}
set :function (newVal){
if(val === newVal){return}
val = newVal;
}
})
}

依赖 收集

创建一个 Dep 类,在 get 中收集依赖,在 set 中新增依赖


class Dep{
constructor(){
this.arr = [] }
addSub(sub){
this.arr.push(sub)
}
removeSub(sub){
remove(this.arr,sub)
}
depend(){
if(window.target){
this.addSub(window.target)
}
}
notify(){
const arrs = this.arr.slice();
for(let i = 0; i< arrs.lenth ;i ++){
arrs[i].update();
}
}

}

function defineReactive(data,key,val){

let dep = new Dep()
Object.definePorperty(data,key,{
enumerable: true,
configurable: true,
get:function(){
dep.depend(); // 收集依赖
return val;
}
set :function (newVal){
if(val === newVal){return}
val = newVal;
dep.notify(); // 新增依赖
}
})
}

Observer 和 Watcher

我们发现 defineReactive 函数只能将某一个属性转换为 get/set 的形式,所以我们需要一个观察者 Observer 用来帮助递归的侦测所有的 key


class Observer{
constructor(value){
this.value = value
}
if(!Array.isArry(value)){
this.walk(value)
}
walk(obj){
const keys = Object.keys(obj)
for(let i = 0; i < keys.length ;i++){
defineReactive(data,keys[i],obj[keys[i])
}
}
}

当这些依赖收集完成之后,我们要通知谁呢?怎么样能让视图知道有变化更新?我们需要实现一个订阅者 Watcher,
每次触发 get 的时候都将 dep 指向自己,这样就可以收集到依赖;
每次 set 的时候都循环调用 Watcher 的 update 方法。