文章目录

// 只有先执行了依赖收集,才能在属性更新的时候派发更新

// 只有先执行了依赖收集,才能在属性更新的时候派发更新

class Dep {
constructor() {
    this.subs = []
}
//添加依赖
addSub(sub) {
    this.subs.push(sub)
}
//更新 
notify() {
    this.subs.forEach(sub=>{
        sub.update()
    })
}
}

Dep.target = null

//当需要依赖收集的时候调用 addSub,当需要派发更新的时候调用 notify

//在组件挂载时会先对所有需要的属性调用 Object.defineProperty()
//然后实例化 Watcher,传入组件更新的回调
//在实例化的过程中会对模板的属性进行求值,触发依赖收集

class Watcher {
constructor(obj,key,cb) {
    // 将 Dep.target 指向自己
    // 然后触发属性的 getter 添加监听
    //最后将 Dep.target 置空
    Dep.target = this
    this.cb = cb
    this.obj = obj 
    this.key = key
    this.value = obj[key] //此处触发属性的 getter
    Dep.target = null
}

update() {
    //获取新值
    this.value = this.obj[this.key]
    //调用 update 更新 Dom
    this.cb(this.value)
}
}

//在执行构造函数时将 Dep.target 指向自身,
//从而收集到对应的 Watcher,在派发    更新的时候取出对应的 Watcher,然后执行 update 函数


function observe(obj) {
if(!obj || typeof obj !== 'object'){
    return
}

for(let key in obj) {
    defineReactive(obj,key,obj[key])
}
}

function defineReactive(obj,key,value) {
observe(value)

let dp = new Dep()
Object.defineProperty(obj,key,{
    enumerable:true,
    cinfigurable:true,
    get:function() {
        if(Dep.target) {
            dp.addSub(Dep.target)
        }
        return value
    },
    set:function(newValue) {
        value = newValue
        dp.notify()
    }
})
}

let obj = {a:1}


observe(obj)

new Watcher(obj,'a',function(v) {
//document.querySelector().innerText = v //更新 DOM
console.log("改变DOM")
})

obj.a = 3 //改变 DOM

我将最后一小段代码与上面分开,因为具体过程用最后一小段代码就可以逐步深入的分析。

首先是声明一个对象,这无需多说。

接着,调用 observe 方法,将 obj 作为参数传入。observe 首先判断 obj 是不是对象,遍历对象的键。然后通过 defineReactive 函数为每个对象的属性添加属性描述符,重点是改变每个属性的 getter 和 setter 行为。具体改变了什么行为我们下面再说。

接下来,新建一个 Watcher 对象,参数是对象、属性以及回调函数。这一部分较难理解。需要结合整体来看。新建 Watcher 对象时,首先调用 Watcher 类中的 constructor。其中先设置 Dep.target 为 this,接着代码中的 obj[key] 触发了 key 的 getter 行为。

在属性的 get 方法中,先检测 Dep.target 有没有值,如果有就通过 Dep 实例的 addSub 添加到实例的数组中,这一步叫做依赖收集。如果声明了多个 Watcher,它们都会被添加到这个实例的数组中,形成一个 Watcher 数组。

我们回到 Watcher 对象,这个对象还有一个 update 方法,调用回调函数并将 obj[key] 这个值传入。

最后对 obj.a 进行赋值时,会调用属性的 set 方法,在 set 后的函数中, Dep 实例的 notify 方法调用了 subs 数组中每个 Watcher 的 update 方法,而 update 方法会调用声明 Watcher 时传入的回调,回调中会进行 DOM 节点的更新。

NASA  2015-11-06 10-26-56 .jpg

文章目录