美文网首页
29,Vue的双向绑定,原理

29,Vue的双向绑定,原理

作者: r8HZGEmq | 来源:发表于2019-11-26 16:14 被阅读0次
view更新data可通过事件监听,重点是data如何更新view
Dep来管理所有的订阅者,一有属性变化就通知他们去update

Object.defineProperty(obj,attribute,{ ... descriptor ... })
第三个参数descriptor可以有如下属性:value、writable、configurable、enumerable、get、set

其中2,3,4个参数默认是false.(writable、configurable、enumerable)
var obj= {}
Object.defineProperty(obj,"long",{
  value:123,
  writable:false,
  enumerable:false,
  configurable:false
})
console.log(obj.long);//123

前面几个参数,大致意思就是,允许更改值,允许遍历obj的属性等配置参数。下面重点要说的是get和set

var obj = {};
Object.defineProperty(obj, 'long', {
  function get(){
    console.log("把值返回出去")
    return 888;
  },
  function set(newVal){
    console.log("赋的新值是:" + newVal);
  }
})
obj.long = 999 /*打印set里的输出语句*/
console.log(obj.long) /*打印get里的输出语句,以及888*/
------------------------------
obj = {
        'brand':'BMW',
        'price':3000
    }
function observable (obj) {
      if (!obj || typeof obj !== 'object') {
            return;
        }
        let keys = Object.keys(obj);
        keys.forEach((key) =>{
            defineReactive(obj,key,obj[key])
        })
        return obj;
    }
------------------------------
function defineReactive (obj,key,val) {
        Object.defineProperty(obj, key, {
            get(){
                console.log(`${key}属性被读取了`);
                return val;
            },
            set(newVal){
                console.log(`${key}属性被修改了`);
                val = newVal;
            }
        })
    }

上面,已经将数据的变化,做了监听和拦截。当属性变化时,我们能知道了。next step,需要通知订阅者,也就是该属性在哪些UI组件上用到了,通知他们去刷新。

核心代码:push(obj),  notify(){foreach:obj.update()}.
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; // 全局唯一的静态属性Watcher。该类的subs也是Watcher的属性

有了Dep订阅管理器,将上面的obj属性监听改造一下。改成真正的监听器Oberver。

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()                    //拦截到数据被改变了,订阅器去主动提醒订阅者去更新
            }
        })
    }

订阅者Watcher,订阅器Dep,监听器Observer

/**
1:首先执行构造函数,执行this.get(),将Watcher自己,存入到订阅器中。dep.target = this.watcher
2:接着执行this.vm.data[this.exp]
  2.1,也就是访问vue对象上的model属性,目的是为了触发监听器Observer里的getter
  2.2,触发了监听器里的get之后,也就会去调订阅器Dep.depend(),addSub(this.watcher)。将订阅者加进去
  2.3,后续,监听器Observer有数据变化的时候触发setter,会执行dep.notify.通知各个subs。也就是各个watcher
3:数据依赖Dep收集完成,对应的UI渲染也要改变。
  3.1,这是要依靠watcher里的update。
  3.2,get到的oldVal和Observer.notify-->sub.update-->watcher.update()的value比较。如果有变化,则cb.call进行更新
*/
class Watcher {
        constructor(vm,exp,cb){ // vm是Vue对象,v-model="exp",cb是watcher绑定的更新函数
            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;
        }
    }

相关文章

网友评论

      本文标题:29,Vue的双向绑定,原理

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