web-dev-qa-db-ja.com

ダーティチェックを使用した双方向データバインディングよりも、一方向データフロー(React、Vue、Angularなど)の方が速いのはなぜですか?

双方向のデータバインディングは高価で遅くなる可能性があることを理解しています。たとえば、食料品とその価格を一覧表示し、価格の合計を表示するおもちゃの食料品リストアプリを想像してください( CodePen )。

[〜#〜] html [〜#〜]

<div ng-app="app">
  <div ng-controller="MainCtrl">
     <table>
       <thead>
         <tr>
           <th>Name</th>
           <th>Price</th>
         </tr>
       </thead>
       <tbody>
         <tr ng-repeat="item in items">
           <td>{{ item.name }}</td>
           <td>
             <input type="number" ng-model="item.price">
           </td>
         </tr>
       </tbody>
    </table>
    <p>Total: {{ getTotal() }}</p>
  </div>
</div>

[〜#〜] js [〜#〜]

angular
  .module('app', [])
  .controller('MainCtrl', function ($scope) {
    $scope.items = [{
      name: 'Milk',
      price: 3.00,
    }, {
      name: 'Eggs',
      price: 2.50
    }, {
      name: 'Bread',
      price: 2.00
    }];
    $scope.getTotal = function () {
      let total = 0;

      $scope.items.forEach(function (item) {
        total += item.price
      });

      return total;
    };
  })
;

ユーザーが乳価入力フィールドに入力するとします。完璧な世界では、Angularは、ミルク入力フィールドに新しい値が反映されていることを確認し、合計を更新することを知っています。しかし、実際には、Angularは「牛乳+パン代」フィールドがある場合はどうなりますか?次に、そのフィールドも更新する必要があります。

したがって、Angularは、ビューに表示されているモデルプロパティのallを通過し、「変更しましたか?そうであれば、このリストが長いと、明らかに時間がかかります。

しかしReactまたはVueのようなものでは、私の印象は同じくらい時間がかかるはずだということです。ここにVue( CodePen ):

[〜#〜] html [〜#〜]

<div id="app">
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in items">
        <td>{{ item.name }}</td>
        <td>
          <input 
            type="number" 
            v-bind:value="item.price" 
            v-on:change="setItemPrice(item, $event)"
          >
        </td>
      </tr>
    </tbody>
  </table>
  <p>Total: {{ total }}</p>
</div>

[〜#〜] js [〜#〜]

new Vue({
  el: '#app',
  computed: {
    total: function () {
      let total = 0;

      this.items.forEach(function (item) {
        total += item.price;
      });

      return total;
    },
  },
  data: {
    items: [{
      name: 'Milk',
      price: 3.00,
    }, {
      name: 'Eggs',
      price: 2.50
    }, {
      name: 'Bread',
      price: 2.00
    }],
  },
  methods: {
    setItemPrice: function (item, event) {
      item.price = Number(event.target.value);
    },
  },
});

私の理解では、変更イベントが発生し、ハンドラメソッド(setItemPrice)の実行が終了すると、Vueは新しい仮想DOMを計算し、以前の仮想DOMと比較します。図実行するDOM変異の最小限のセットを取り出し、実行します。

DOMをトラバースし、違いがあるかどうかを確認するプロセスには、ダーティチェック中にウォッチャー配列をトラバースするプロセスと少なくとも同じ時間がかかると思います。もう少し時間がかかると思います。 HTMLを見てください。

<table>
  <thead>...</thead>
  <tbody>
    <tr>
      <td>Milk</td>
      <td>3</td>
    </tr>
    <tr>
      <td>Bread</td>
      <td>2.5</td>
    </tr>
    <tr>
      <td>Eggs</td>
      <td>2</td>
    </tr>
  </tbody>
</table>
<p>Total: 7.5</p>

n =食料品リストのサイズの場合、ダーティーチェックは、watchers配列のnアイテムを反復処理する必要があります。ただし、DOMをトラバースする場合は、n<tr>タグplusを繰り返し処理する必要があります。

ダーティチェックを使用した双方向データバインディングよりも一方向データフローのパフォーマンスが大幅に高い(と見なされる)理由について何が欠けていますか?

4
Adam Zerner

あなたは別のものを比較しようとしていると思いますが、私はあなたの意味を理解しています。私はあなたの質問を次のように言い換えます:なぜAngularJSでのダーティーチェックはDOM差分の構築より遅いのですか?.

ちなみに、Angular2 +はテンプレート式で使用される値もダーティチェックします。ボタンをクリックすると、angular次に変更検出サイクルが実行され、上部のコンポーネントから最下部に移動します。これにより、式で使用されているすべてのモデル値がチェックされます。以前の値を参照します。そのため、値をダーティチェックします。AngularJSとの違いは、2度チェックしないことです。また、詳細な比較も行いません(AngularJSではデフォルトで無効になっていますが、次のようにオンにすることができます) $ watch関数の3番目の引数)。

しかし、ダーティーチェックとDOM差分の構築を比較することは少し間違っています。それらは異なるものであり、実際にそれらを組み合わせることができます。

そのときの違いは何ですか。ダーティーチェックでは、すべての変更またはイベントのすべてをチェックする必要があります。それは非常に高性能である可能性があり、Angular2 +はそれを証明します。しかし、Reactは、変更されたコンポーネントを正確に把握しているため、setState()をトリガーします。そのため、アプリ内のすべてをチェックする代わりに、現在の差分を構築しようとしますコンポーネントツリー。

したがって、結論として、ダーティーチェックは悪くありません。実装とアプリケーションによって異なります。アプリケーションでは、diffを作成するよりもダーティチェック値を使用する方が速い場合があります。 ここ は、React/View/Angular5の興味深いパフォーマンス比較です

4
Pavel