美文网首页微信小程序开发技术干货JavaScript 进阶营
前端框架响应式简单实现及代码分析

前端框架响应式简单实现及代码分析

作者: 老邵 | 来源:发表于2019-10-21 21:25 被阅读0次
    // 只有先执行了依赖收集,才能在属性更新的时候派发更新
    
    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

    相关文章

      网友评论

        本文标题:前端框架响应式简单实现及代码分析

        本文链接:https://www.haomeiwen.com/subject/uqjxvctx.html