什么是双向数据绑定

原理

  • View(视图)的变化可以实时的让Model(数据)发生变化,同样的,Model的变化也是实时更新到View上。
  • Vue2.x采用数据劫持和发布订阅模式实现双向数据绑定,通过ES5提供的Object.defineProperty()方法来劫持(监控)对象属性的gettersetter, 并在数据(对象)发生变动时通知订阅者,触发相应的监听回调。由于是在不同的数据上触发同步,可以精确的将变更发送给绑定的视图,而不是对所有数据都执行一次检测。

简易实现

1
2
手机号: <input id="tel" type="tel" />
<p>输入的手机号为:<span id="num"></span></p>
  • Object.defineProperty()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    let obj = {
    tel: null
    };
    let newObj = JSON.parse(JSON.stringify(obj));

    Object.defineProperty(obj, 'tel', {
    get() {
    return newObj.tel;
    },
    set(val) {
    if (val === newObj.tel) return;

    newObj.tel = val;

    observer();
    }
    });

    function observer() {
    document.getElementById('num').innerHTML = obj.tel;
    document.getElementById('tel').val = obj.tel;
    }

    observer();

    setTimeout(() => {
    obj.tel = '15122222222';
    }, 1000);

    document.getElementById('tel').oninput = function () {
    obj.tel = this.value;
    }
  • Proxy

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    let obj = {};

    obj = new Proxy(obj, {
    get(target, prop) {
    return target[prop];
    },
    set(target, prop, value) {
    target[prop] = value;
    observer();
    }
    });

    function observer() {
    document.getElementById('num').innerHTML = obj.tel;
    document.getElementById('tel').val = obj.tel;
    }

    observer();

    setTimeout(() => {
    obj.tel = '15122222222';
    }, 1000);

    document.getElementById('tel').oninput = function () {
    obj.tel = this.value;
    }

差异比较

Object.defineProperty()
优点:

  • 不需要显式的调用,Vue利用数据劫持和发布订阅,可以直接感知变化并且驱动视图
  • 直接得到精确地变化数据,劫持了属性的setter,当属性值改变我们可以精确地获取变化的内容 newVal,不需要额外的diff操作

    缺点:

  • 不能监听数组:因为数组没有getter setter,数组长度不确定,如果太长则影响性能
  • 只能监听属性,而不是整个对象;需要遍历属性
  • 只能监听属性变化,不能监听属性的删除

    Proxy
    优点:

  • 可以监听数据
  • 可以监听整个对象(无需遍历)
  • 13种拦截方法,很强大
  • 返回新对象而不是直接修改原对象,更符合immutable

    缺点:

  • 兼容性稍弱,无法使用polyfill