javascript實現資料雙向繫結的三種方式小結,javascript三種方式

來源:互聯網
上載者:User

javascript實現資料雙向繫結的三種方式小結,javascript三種方式

前端資料的雙向繫結方法

前端的視圖層和資料層有時需要實現雙向繫結(two-way-binding),例如mvvm架構,資料驅動視圖,檢視狀態機等,研究了幾個目前主流的資料雙向繫結架構,總結了下。目前實現資料雙向繫結主要有以下三種。

1、手動綁定

比較老的實現方式,有點像觀察者編程模式,主要思路是通過在資料對象上定義get和set方法(當然還有其它方法),調用時手動調用get或set資料,改變資料後出發UI層的渲染操作;以視圖驅動資料變化的情境主要應用與input、select、textarea等元素,當UI層變化時,通過監聽dom的change,keypress,keyup等事件來出發事件改變資料層的資料。整個過程均通過函數調用完成。

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>data-binding-method-set</title></head><body>  <input q-value="value" type="text" id="input">  <div q-text="value" id="el"></div>  <script>    var elems = [document.getElementById('el'), document.getElementById('input')];    var data = {      value: 'hello!'    };    var command = {      text: function(str){        this.innerHTML = str;      },      value: function(str){        this.setAttribute('value', str);      }    };    var scan = function(){          /**       * 掃描帶指令的節點屬性       */      for(var i = 0, len = elems.length; i < len; i++){        var elem = elems[i];        elem.command = [];        for(var j = 0, len1 = elem.attributes.length; j < len1; j++){          var attr = elem.attributes[j];          if(attr.nodeName.indexOf('q-') >= 0){            /**             * 調用屬性指令,這裡可以使用資料改變檢測             */            command[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue]);            elem.command.push(attr.nodeName.slice(2));          }        }      }    }    /**     * 設定資料後掃描     */    function mvSet(key, value){      data[key] = value;      scan();    }    /**     * 資料繫結監聽     */    elems[1].addEventListener('keyup', function(e){      mvSet('value', e.target.value);    }, false);    scan();    /**     * 改變資料更新視圖     */    setTimeout(function(){      mvSet('value', 'fuck');    },1000)  </script></body></html>

2、髒檢查機制

以典型的mvvm架構angularjs為代表,angular通過檢查髒資料來進行UI層的操作更新。關於angular的髒檢測,有幾點需要瞭解些: - 髒檢測機制並不是使用定時檢測。 - 髒檢測的時機是在資料發生變化時進行。 - angular對常用的dom事件,xhr事件等做了封裝, 在裡面觸發進入angular的digest流程。 - 在digest流程裡面, 會從rootscope開始遍曆, 檢查所有的watcher。 (關於angular的具體設計可以看其他文檔,這裡只討論資料繫結),那我們看下髒檢測該如何去做:主要是通過設定的資料來需找與該資料相關的所有元素,然後再比較資料變化,如果變化則進行指令操作

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>data-binding-drity-check</title></head><body>  <input q-event="value" ng-bind="value" type="text" id="input">  <div q-event="text" ng-bind="value" id="el"></div>  <script>  var elems = [document.getElementById('el'), document.getElementById('input')];    var data = {    value: 'hello!'  };  var command = {    text: function(str) {      this.innerHTML = str;    },    value: function(str) {      this.setAttribute('value', str);    }  };  var scan = function(elems) {    /**     * 掃描帶指令的節點屬性     */    for (var i = 0, len = elems.length; i < len; i++) {      var elem = elems[i];      elem.command = {};      for (var j = 0, len1 = elem.attributes.length; j < len1; j++) {        var attr = elem.attributes[j];        if (attr.nodeName.indexOf('q-event') >= 0) {          /**           * 調用屬性指令           */          var dataKey = elem.getAttribute('ng-bind') || undefined;          /**           * 進行資料初始化           */          command[attr.nodeValue].call(elem, data[dataKey]);          elem.command[attr.nodeValue] = data[dataKey];        }      }    }  }  /**   * 髒迴圈檢測   * @param {[type]} elems [description]   * @return {[type]}    [description]   */  var digest = function(elems) {    /**     * 掃描帶指令的節點屬性     */    for (var i = 0, len = elems.length; i < len; i++) {      var elem = elems[i];      for (var j = 0, len1 = elem.attributes.length; j < len1; j++) {        var attr = elem.attributes[j];        if (attr.nodeName.indexOf('q-event') >= 0) {          /**           * 調用屬性指令           */          var dataKey = elem.getAttribute('ng-bind') || undefined;          /**           * 進行髒資料檢測,如果資料改變,則重新執行指令,否則跳過           */          if(elem.command[attr.nodeValue] !== data[dataKey]){            command[attr.nodeValue].call(elem, data[dataKey]);            elem.command[attr.nodeValue] = data[dataKey];          }        }      }    }  }  /**   * 初始化資料   */  scan(elems);  /**   * 可以理解為做資料劫持監聽   */  function $digest(value){    var list = document.querySelectorAll('[ng-bind='+ value + ']');    digest(list);  }  /**   * 輸入框資料繫結監聽   */  if(document.addEventListener){    elems[1].addEventListener('keyup', function(e) {      data.value = e.target.value;      $digest(e.target.getAttribute('ng-bind'));    }, false);  }else{    elems[1].attachEvent('onkeyup', function(e) {      data.value = e.target.value;      $digest(e.target.getAttribute('ng-bind'));    }, false);  }  setTimeout(function() {    data.value = 'fuck';    /**     * 這裡問啥還要執行$digest這裡關鍵的是需要手動調用$digest方法來啟動髒檢測     */    $digest('value');  }, 2000)  </script></body></html>

3、前端資料劫持(Hijacking)

第三種方法則是avalon等架構使用的資料劫持方式。基本思路是使用Object.defineProperty對資料對象做屬性get和set的監聽,當有資料讀取和賦值操作時則調用節點的指令,這樣使用最通用的=等號賦值就可以了。具體實現如下:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>data-binding-hijacking</title></head><body>  <input q-value="value" type="text" id="input">  <div q-text="value" id="el"></div>  <script>  var elems = [document.getElementById('el'), document.getElementById('input')];  var data = {    value: 'hello!'  };  var command = {    text: function(str) {      this.innerHTML = str;    },    value: function(str) {      this.setAttribute('value', str);    }  };  var scan = function() {    /**     * 掃描帶指令的節點屬性     */    for (var i = 0, len = elems.length; i < len; i++) {      var elem = elems[i];      elem.command = [];      for (var j = 0, len1 = elem.attributes.length; j < len1; j++) {        var attr = elem.attributes[j];        if (attr.nodeName.indexOf('q-') >= 0) {          /**           * 調用屬性指令           */          command[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue]);          elem.command.push(attr.nodeName.slice(2));        }      }    }  }  var bValue;  /**   * 定義屬性設定劫持   */  var defineGetAndSet = function(obj, propName) {    try {      Object.defineProperty(obj, propName, {        get: function() {          return bValue;        },        set: function(newValue) {          bValue = newValue;          scan();        },        enumerable: true,        configurable: true      });    } catch (error) {      console.log("browser not supported.");    }  }  /**   * 初始化資料   */  scan();  /**   * 可以理解為做資料劫持監聽   */  defineGetAndSet(data, 'value');  /**   * 資料繫結監聽   */  if(document.addEventListener){    elems[1].addEventListener('keyup', function(e) {      data.value = e.target.value;    }, false);  }else{    elems[1].attachEvent('onkeyup', function(e) {      data.value = e.target.value;    }, false);  }  setTimeout(function() {    data.value = 'fuck';  }, 2000)  </script></body></html>

但值得注意的是defineProperty支援IE8以上的瀏覽器,這裡可以使用__defineGetter__ 和 __defineSetter__ 來做相容但是瀏覽器安全色性的原因,直接用defineProperty就可以了。至於IE8瀏覽器仍需要使用其它方法來做hack。如下代碼可以對IE8進行hack,defineProperty支援IE8。例如使用es5-shim.js就可以了。(IE8以下瀏覽器忽略)

4、小結

首先這裡的例子只是簡單的實現,讀者可以深入感受三種方式的異同點,複雜的架構也是通過這樣的基本思路滾雪球滾大的。

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.