1. 程式人生 > >Vue-資料繫結原理

Vue-資料繫結原理

VueJS 使用 ES5 提供的 Object.defineProperty() 方法實現資料繫結。
感覺實現時主要是在defineProperty的set和get上做了很多文章,在get中確定了data和view的依賴關係,這樣在data改呼叫set時就可以根據依賴修改view。
Object.defineProperty() - JavaScript | MDN

確定data和view控制元件(watcher)的依賴關係(dep):get

<!DOCTYPE html>
<html lang="en">
<head>
  
  <title>Document</title>
  <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
  </head>
<body>
  輸入值:<input id="val" type="text"><br>
  文字值:<span id="text"></span><br>
  <script>
    var data = {};
    var input = 'default';
    var dep = [];
    var watcher = null;
    // 對data的input進行繫結
    Object.defineProperty(data, 'input', {
      // 獲取obj.input時呼叫,獲取到的obj.input的值為get函式return的值
      get: function(){
        // 如果有watcher就新增到dep中(與data的input繫結)
        if (watcher) {
          dep.push(watcher);
        }
        return input;
      }
    });

    // 找到view中與data.input繫結的控制元件進行繫結
    // 第一個觀察者
    watcher = {jq: $('#val'), type: 'val'};
    watcher.jq.val(data.input);
    watcher.jq.keyup(function(){    
      data.input = $(this).val();
    });
    watcher = null;
    
    // 第二個觀察者
    watcher =  {jq: $('#text'), type: 'text'};
    watcher.jq.text(data.input);
    watcher = null;
    console.log(dep);

    /*
    //用jQuery實現:
    var data = {};
    var input = 'default';
    var dep = [];
    var watcher = null;
    
    // 第一個觀察者
    watcher = {jq: $('#val'), type: 'val'};
    dep.push(watcher);
    watcher.jq.val(input);
    watcher.jq.keyup(function(){    
      data.input = $(this).val();
    });

    // 第二個觀察者
    watcher =  {jq: $('#text'), type: 'text'};
    dep.push(watcher);
    watcher.jq.text(input);
    console.log(dep);
    */
  </script>
</body>
</html>

監聽data的改變,更新依賴於(depend on)data的view控制元件(watcher):set

<!DOCTYPE html>
<html lang="en">
<head>
  
  <title>Document</title>
  <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
  </head>
<body>
  輸入值:<input id="val" type="text"><br>
  文字值:<span id="text"></span><br>
  <button id="get">假設從後臺請求資料了</button>
  <script>
    var data = {};
    var input = 'default';
    var dep = [];
    var watcher = null;
    // 對data的input進行繫結
    Object.defineProperty(data, 'input', {
      // 獲取obj.input時呼叫,獲取到的obj.input的值為get函式return的值
      get: function(){
        // 如果有watcher就新增到dep中(與data的input繫結)
        if (watcher) {
          dep.push(watcher);
        }
        return input;
      },
      /* 
      對obj.input賦值時呼叫,val是準備賦值的值
      獲取obj.input時會呼叫get,沒有get會返回undefined
      (如果只想obj的input《本身》賦值可以設定writable: true,value: 預設值,
      但這時再設定get和set就會產生異常)
      */
      set: function(val){
        if (val != input) {
          input = val;
          $(dep).each(function(){
            if (this.type = 'val') {
              this.jq.val(input);
            }
            if (this.type = 'text') {
              this.jq.text(input);
            }
          });
        }
      }
    });

    // 找到view中與data.input繫結的控制元件進行繫結
    // 第一個觀察者
    watcher = {jq: $('#val'), type: 'val'};
    watcher.jq.val(data.input);
    watcher.jq.keyup(function(){    
      data.input = $(this).val();
    });
    watcher = null;

    // 第二個觀察者
    watcher =  {jq: $('#text'), type: 'text'};
    watcher.jq.text(data.input);
    watcher = null;
    console.log(dep);

    $('#get').click(function(){
      data.input = '後臺的資料';
    });

    /*
    //用jQuery實現:
    var data = {};
    var input = 'default';
    var dep = [];
    var watcher = null;
    
    // 第一個觀察者
    watcher = {jq: $('#val'), type: 'val'};
    dep.push(watcher);
    watcher.jq.val(input);
    watcher.jq.keyup(function(){
      var input = $(this).val();
      $(dep).each(function(){
        if (this.type = 'val') {
          this.jq.val(input);
        }
        if (this.type = 'text') {
          this.jq.text(input);
        }
      });    
    });

    // 第二個觀察者
    watcher =  {jq: $('#text'), type: 'text'};
    dep.push(watcher);
    watcher.jq.text(input);
    console.log(dep);

    $('#get').click(function(){
      var input = '後臺的資料'
      $(dep).each(function(){
        if (this.type = 'val') {
          this.jq.val(input);
        }
        if (this.type = 'text') {
          this.jq.text(input);
        }
      });
    });
    */
  </script>
</body>
</html>