美文网首页工作生活
浅谈vue双向绑定的原理-back

浅谈vue双向绑定的原理-back

作者: 苏本的书柜 | 来源:发表于2019-07-04 11:32 被阅读0次

    简易说明

    Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通过watch观测通知视图更新
    

    MVVM是什么

    数据变化更新视图,视图变换更新数据
    

    如何让数据变得可以观测呢

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
    

    如何起作用的呢

    let car = {} let val = 3000 Object.defineProperty(car, 'price', { get(){ console.log('price属性被读取了') return val }, set(newVal){ console.log('price属性被修改了') val = newVal } })
    
    然后你对car进行赋值以及读写都会触发get或者set的函数
    

    一个简单的双向绑定函数

    /** * 把一个对象的每一项都转化成可观测对象 * 
    @param { Object } obj 对象 */ 
    function observable (obj) { if (!obj || typeof obj !== 'object') { return; } let keys = Object.keys(obj); keys.forEach((key) =>{ defineReactive(obj,key,obj[key]) }) return obj; } 
    /** * 使一个对象转化成可观测对象 * 
    @param { Object } obj 对象 * 
    @param { String } key 对象的key * 
    @param { Any } val 对象的某个key的值 */
     function defineReactive (obj,key,val) { Object.defineProperty(obj, key, { get(){ console.log(`${key}属性被读取了`); return val; }, set(newVal){ console.log(`${key}属性被修改了`); val = newVal; } }) }
    
    
    

    因为被拦截,所以就会定义出来一个观测的属性

    依赖收集,根据订阅者-观察模式,创建一个消息收集模式

    class Dep { constructor(){ this.subs = [] }, //增加订阅者 addSub(sub){ this.subs.push(sub); }, //判断是否增加订阅者 depend () { if (Dep.target) { this.addSub(Dep.target) } }, //通知订阅者更新 notify(){ this.subs.forEach((sub) =>{ sub.update() }) } }Dep.target = null;
    

    修改objectdefineProperty的观察者模式

    function defineReactive (obj,key,val) { let dep = new Dep(); Object.defineProperty(obj, key, { get(){ dep.depend(); console.log(`${key}属性被读取了`); return val; }, set(newVal){ val = newVal; console.log(`${key}属性被修改了`); dep.notify() //数据变化通知所有订阅者 } }) }
    

    创建一个watch订阅者

    class Watcher { constructor(vm,exp,cb){ this.vm = vm; this.exp = exp; this.cb = cb; this.value = this.get(); // 将自己添加到订阅器的操作 }, update(){ let value = this.vm.data[this.exp]; let oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value, oldVal); }, get(){ Dep.target = this; // 缓存自己 let value = this.vm.data[this.exp] // 强制执行监听器里的get函数 Dep.target = null; // 释放自己 return value; } }
    

    相关文章

      网友评论

        本文标题:浅谈vue双向绑定的原理-back

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