1. 程式人生 > >Vue是如何實現雙向數據綁定的

Vue是如何實現雙向數據綁定的

created 點綁定 target fin 數據改變 賦值 eve 通知 NPU

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 收到通知,視圖也將發生改變。

Vue是如何實現雙向數據綁定的