Vue是如何实现双向数据绑定的

1、实现双向绑定的基本原理

vue实现数据双向绑定主要是采用数据劫持结合发布者-订阅者模式的方式。

数据劫持是通过Object.defineProperty()实现的,该函数为每个属性添加setter,getter 的方法,在数据发生改变时 setter 方法会被触发,然后发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 为每个属性添加 setter,getter 的方法。

vue的数据双向绑定主要通过三个模块完成:观察者Observer、订阅者Watcher、Compile解析模板指令。

(1)Observer监听 model 的数据变化,如果有变动的,就通知订阅者

(2)初始渲染页面、为节点绑定函数:通过 Compile 扫描和解析每个节点的相关指令,将模板中的变量替换成数据,并根据初始数据渲染页面视图。并且将每个指令对应的节点绑定函数,一旦视图交互,绑定的函数被触发,然后触发setter 方法,订阅者就会收到通知。

(3)watcher 搭起了 observer 和 Compile 之间的通信桥梁,达到数据变化 —>视图更新,视图交互变化(input)—>数据 model 发生变更的双向绑定效果。

var vm = new Vue({ 
  data: { 
    obj: { a:
1 }
  },   created:
function () {
    console.log(
this.obj);
  }
});

打印Vue实例的data里的某个数据的某个属性,可以看到该属性含有 setter、getter 方法,由此可以得知,每个属性都被添加了setter、getter 方法。

1.1、思路分析

实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据:

view更新data通过事件监听即,比如 input 标签监听 'input' 事件就可以实现。关键点在于 data 如何更新view,当数据改变,如何更新视图的。重点是如何知道数据变了,而这可以由Observer实现,通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,然后只要将一些需要更新视图的方法放在这里面就可以实现data更新view了。

2、Observer 的实现

利用Obeject.defineProperty()来为每个属性添加setter,getter 的方法实现监听属性变动。将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 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]);
    });
  };

  function defineReactive(data, key, val) {
    observe(val); // 监听子属性
    Object.defineProperty(data, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      get: function () {
        return val;
      },
      set: function (newVal) {
        console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
        val = newVal;
      }
    });
  }

通过上面代码就可以实现对每个属性进行监听。

 3、JS实现简单的双向绑定

<body>
    <div id="app">
    <input type="text" id="txt">
    <p id="show"></p>
</div>
</body>
<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>

上面代码中,首先 defineProperty 为每个属性添加 getter、setter 方法,当数据发生改变, setter 方法被触发,视图也发生改变。setter 里面的执行命令可以看做是一个订阅者 Watcher,将视图和数据连接起来了。最下面的代码为节点绑定方法可以看做是Compile的作用,为指令节点绑定方法,当发生视图交互时,函数被触发,数据被改变,Watcher 收到通知,视图也将发生改变。

猜你喜欢

转载自www.cnblogs.com/wenxuehai/p/10507694.html