Vue实现数据双向绑定

1、Vue实现数据双向绑定的原理

Object.defineProperty()

2、vue实现数据双向绑定主要步骤:

  • 2.1 实现Observer
    对于需要观察数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。
var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq';     // 哈哈哈,监听到值变化了 kindeng --> dmq

//遍历所有属性
function observe(data) {      
    if (!data || typeof data !== 'object') {
        return;
    }
   Object.keys(data).forEach(function(key) {
        defineReactive(data, key, data[key]);      //添加get、set方法
    });
};

// 为每个属性添加get 、set 方法
function defineReactive(data, key, val) {
    var dep = new Dep();
    observe(val); // 监听子属性(也就是说如果子属性也是一个对象的话,继续为其添加get、set方法,以作监听)
    Object.defineProperty(data, key, {
        enumerable: true, // 可枚举
        configurable: false, // 不能再define
        get: function() {
            return val;
        },
        set: function(newVal) {
             if (val === newVal) return;
            val = newVal;
            dep.notify(); // 通知所有订阅者
        }
    });
}

//记录并通知订阅者
function Dep() {
    this.subs = [];
}
Dep.prototype = {
    addSub: function(sub) {
        this.subs.push(sub);
    },
    notify: function() {
        this.subs.forEach(function(sub) {
            sub.update(); // 调用订阅者的update方法,通知变化
        });
    }
};


//这样我们已经可以监听每个数据的变化了,那么监听到变化之后就是怎么通知订阅者了,
//所以接下来我们需要实现一个消息订阅器,很简单,维护一个数组,用来收集订阅者,
//数据变动触发notify,再调用订阅者的update方法,代码改善之后是这样:
  • 2.2、实现Compile
    compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
function Compile(el) {
    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
    if (this.$el) {
        this.$fragment = this.node2Fragment(this.$el);
        this.init();
        this.$el.appendChild(this.$fragment);
    }
}
Compile.prototype = {
    init: function() { this.compileElement(this.$fragment); },
    node2Fragment: function(el) {
        var fragment = document.createDocumentFragment(), child;
        // 将原生节点拷贝到fragment
        while (child = el.firstChild) {
            fragment.appendChild(child);
        }
        return fragment;
    }
};
.......................................
  • 2.3、实现Watcher
    Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    ①在自身实例化时往属性订阅器(dep)里面添加自己
    ②自身必须有一个update()方法
    ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
function Watcher(vm, exp, cb) {
    this.cb = cb;
    this.vm = vm;
    this.exp = exp;
    // 此处为了触发属性的getter,从而在dep添加自己,结合Observer更易理解
    this.value = this.get(); 
}
Watcher.prototype = {
    update: function() {
        this.run();    // 属性值变化收到通知
    },
    run: function() {
        var value = this.get(); // 取到最新值
        var oldVal = this.value;
        if (value !== oldVal) {
            this.value = value;
            this.cb.call(this.vm, value, oldVal); // 执行Compile中绑定的回调,更新视图
        }
    },
    get: function() {
        Dep.target = this;    // 将当前订阅者指向自己
        var value = this.vm[exp];    // 触发getter,添加自己到属性订阅器中
        Dep.target = null;    // 添加完毕,重置
        return value;
    }
};

3、简单实现案例


   

4、面试双向绑定回答范例

第一步:实现observer,监听数据变化,同时创建订阅器Dep,集中管理订阅者,一旦数据发生变化,触发set方法,由订阅器Dep通知订阅者。

首先对需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,就能通知订阅者数据发生了变化,触发更新。

第二步:实现compile,即对template模板进行解析,为模板中的每个指令绑定相应的更新函数。
compile的主要作用就是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数

第三步:实现Watcher,即Observer和Compile之间通信的桥梁,能够 为该属性添加订阅 并 收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
Watcher是Observer和Compile之间通信的桥梁,主要做的事情是:
1、为该属性添加订阅
2、收到每个属性变动的通知,执行指令绑定的相应回调函数

Vue实现数据双向绑定_第1张图片

参考链接 https://www.cnblogs.com/alongup/p/9022180.html
参考链接 https://www.chuchur.com/article/vue-mvvm-observer

你可能感兴趣的:(Vue,vue双向绑定原理详解,v-model,双向绑定)