ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript で双方向データ バインディングを実装する 3 つのメソッドについて共有する

JavaScript で双方向データ バインディングを実装する 3 つのメソッドについて共有する

黄舟
黄舟オリジナル
2017-03-21 14:41:001932ブラウズ

この記事では、主に javascript 双方向データ バインディングを実現する 3 つの方法の概要を紹介します。フロントエンド ビュー層 は、現在、双方向データ バインディングを実装する必要がある場合があります。興味のある方は双方向データバインディングを実装してください。

フロントエンドデータの双方向バインディング方法

フロントエンドのビュー層とデータ層は、mvvmフレームワーク、データなどの双方向バインディング(双方向バインディング)を実装する必要がある場合があります。 -ドリブン ビュー、ビュー ステート マシンなど、現在主流のいくつかの双方向データ バインディング フレームワークがまとめられています。現在、双方向データ バインディングを実装するには主に 3 つの方法があります。

1. 手動バインディング

古い実装メソッドは、オブザーバープログラミングモードに少し似ています。主なアイデアは、データオブジェクトに get メソッドと set メソッドを定義することです。呼び出し時に get または set data を手動で呼び出し、データを変更した後に UI レイヤーのレンダリング操作を開始します。ビューがデータ変更を駆動するシーンは、主に input、select、textarea などの要素に適用されます。 dom、keypress、keyup、その他のイベントの変化を監視して、データ層のデータを変更するイベントをトリガーすることで、UI 層が変化します。プロセス全体は関数呼び出しによって完了します。

<!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">
  <p q-text="value" id="el"></p>
  <script>
    var elems = [document.getElementById(&#39;el&#39;), document.getElementById(&#39;input&#39;)];

    var data = {
      value: &#39;hello!&#39;
    };

    var command = {
      text: function(str){
        this.innerHTML = str;
      },
      value: function(str){
        this.setAttribute(&#39;value&#39;, 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(&#39;q-&#39;) >= 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(&#39;keyup&#39;, function(e){
      mvSet(&#39;value&#39;, e.target.value);
    }, false);

    scan();

    /**
     * 改变数据更新视图
     */
    setTimeout(function(){
      mvSet(&#39;value&#39;, &#39;fuck&#39;);
    },1000)

  </script>
</body>
</html>

2. ダーティ チェック メカニズム

典型的な mvvm フレームワークangularjs で代表される、angular はダーティ データをチェックすることで UI レイヤーの操作を更新します。 Angular のダーティ検出については、次のことを知っておく必要があります。 - ダーティ検出メカニズムは、スケジュールされた検出を使用しません。・ダーティ検出のタイミングはデータ変化時です。 - Angular は、一般的に使用される DOM イベント、XHR イベントなどをカプセル化し、Angular に入るダイジェスト プロセスをトリガーします。 - ダイジェスト プロセスでは、ルートスコープからトラバースし、すべてのウォッチャーをチェックします。 (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">
  <p q-event="text" ng-bind="value" id="el"></p>
  <script>

  var elems = [document.getElementById(&#39;el&#39;), document.getElementById(&#39;input&#39;)];
  
  var data = {
    value: &#39;hello!&#39;
  };

  var command = {
    text: function(str) {
      this.innerHTML = str;
    },
    value: function(str) {
      this.setAttribute(&#39;value&#39;, 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(&#39;q-event&#39;) >= 0) {
          /**
           * 调用属性指令
           */
          var dataKey = elem.getAttribute(&#39;ng-bind&#39;) || 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(&#39;q-event&#39;) >= 0) {
          /**
           * 调用属性指令
           */
          var dataKey = elem.getAttribute(&#39;ng-bind&#39;) || 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(&#39;[ng-bind=&#39;+ value + &#39;]&#39;);
    digest(list);
  }

  /**
   * 输入框数据绑定监听
   */
  if(document.addEventListener){
    elems[1].addEventListener(&#39;keyup&#39;, function(e) {
      data.value = e.target.value;
      $digest(e.target.getAttribute(&#39;ng-bind&#39;));
    }, false);
  }else{
    elems[1].attachEvent(&#39;onkeyup&#39;, function(e) {
      data.value = e.target.value;
      $digest(e.target.getAttribute(&#39;ng-bind&#39;));
    }, false);
  }

  setTimeout(function() {
    data.value = &#39;fuck&#39;;
    /**
     * 这里问啥还要执行$digest这里关键的是需要手动调用$digest方法来启动脏检测
     */
    $digest(&#39;value&#39;);
  }, 2000)

  </script>
</body>
</html>

3. フロントエンドデータハイジャック(ハイジャック)

3番目の方法は、avalonなどのフレームワークで使用されるデータハイジャック方法です。基本的な考え方は、Object.defineProperty を使用して、データ オブジェクトのプロパティの取得と設定を監視することです。このようにして、最も一般的な = 等号の割り当てを使用できます。 。具体的な実装は次のとおりです:

<!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">
  <p q-text="value" id="el"></p>
  <script>


  var elems = [document.getElementById(&#39;el&#39;), document.getElementById(&#39;input&#39;)];

  var data = {
    value: &#39;hello!&#39;
  };

  var command = {
    text: function(str) {
      this.innerHTML = str;
    },
    value: function(str) {
      this.setAttribute(&#39;value&#39;, 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(&#39;q-&#39;) >= 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, &#39;value&#39;);

  /**
   * 数据绑定监听
   */
  if(document.addEventListener){
    elems[1].addEventListener(&#39;keyup&#39;, function(e) {
      data.value = e.target.value;
    }, false);
  }else{
    elems[1].attachEvent(&#39;onkeyup&#39;, function(e) {
      data.value = e.target.value;
    }, false);
  }

  setTimeout(function() {
    data.value = &#39;fuck&#39;;
  }, 2000)
  </script>
</body>

</html>

ただし、defineProperty は IE8 以降のブラウザをサポートしていることに注意してください。ただし、ブラウザの互換性のため、defineProperty を直接使用してください。 IE8 ブラウザに関しては、ハッキングするには他の方法を使用する必要があります。次のコードは IE8 をハッキングする可能性があり、defineProperty は IE8 をサポートします。たとえば、es5-shim.js を使用してください。 (IE8 未満のブラウザでは無視されます)

4. 概要

まず第一に、ここでの例は単純な実装にすぎませんが、読者は、この基本的な方法を通じて、複雑なフレームワークの類似点と相違点を深く感じることができます。アイデア。 。

以上がJavaScript で双方向データ バインディングを実装する 3 つのメソッドについて共有するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。