Perché il flusso di dati a senso unico (ad esempio React, Vue, Angular) è più rapido del bind di dati bidirezionale con controllo sporco?

1

Comprendo che l'associazione dei dati bidirezionale può essere costosa e lenta. Ad esempio, immagina un'app della lista della spesa che ti consente di elencare gli articoli della spesa e i loro prezzi e ti mostra la somma dei prezzi ( 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;
    };
  })
;

Immagina che l'utente digiti nel campo di inserimento del prezzo del latte. In un mondo perfetto, Angular saprebbe assicurarsi che il campo di input del latte rifletta il nuovo valore e aggiorni il totale. Ma in realtà, Angular non sa che quelle sono le uniche cose da aggiornare. Cosa succede se ho un campo "latte + costo del pane"? Poi quel campo avrebbe bisogno di essere aggiornato anche.

Quindi Angular passa attraverso tutti delle proprietà del modello che vengono visualizzate nella vista e chiede "Sei cambiato? Se è così, ti rosterò". Se questa lista è lunga, ovviamente ci vorrà molto tempo.

Ma in qualcosa come React o Vue, la mia impressione è che dovrebbe richiedere un tempo ugualmente lungo. Ecco un'implementazione della stessa app giocattolo in 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);
    },
  },
});

La mia comprensione è che dopo che l'evento change è stato attivato e che il metodo del gestore ( setItemPrice ) ha terminato l'esecuzione, Vue calcolerà un nuovo DOM virtuale, differendolo con il DOM virtuale precedente, individuando il set minimo di mutazioni DOM da eseguire e quindi eseguirli.

Penso che il processo di attraversamento del DOM e vedere se ci sono delle differenze richiederebbe almeno il tempo necessario per attraversare l'array degli osservatori durante il controllo sporco. La mia impressione è che ci vorrebbe più tempo. Guarda l'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>

Se n = la dimensione della lista della spesa, la verifica sporca deve scorrere attraverso n elementi nella matrice degli osservatori. Ma nel percorrere il DOM, devi scorrere i n <tr> tag più tutto il resto.

Che cosa mi manca del motivo per cui il flusso di dati a senso unico è (visto come) significativamente più performante rispetto al collegamento bidirezionale dei dati con il controllo sporco?

    
posta Adam Zerner 08.06.2018 - 23:13
fonte

1 risposta

1

Immagino che tu stia cercando di confrontare cose diverse, ma capisco cosa intendi. Vorrei riformulare la tua domanda a: Perché il controllo sporco in AngularJS è più lento rispetto alla creazione di differenze DOM.

A proposito, Angular2 + controlla anche i valori usati nelle espressioni template. Diciamo che fai clic su un pulsante, quindi esegui il ciclo di rilevamento del cambiamento angolare e passa dal componente superiore a quello inferiore e ciò che sta facendo è il controllo di tutti i valori del modello utilizzati nelle espressioni per riferimento con i loro valori precedenti. Quindi sporco controlla i valori. La differenza con AngularJS è che non lo controlla due volte. Inoltre, non effettua il confronto approfondito (che è anche disabilitato in AngularJS per impostazione predefinita, ma potrebbe essere attivato come terzo argomento della funzione $ watch).

Ma paragonare il controllo sporco e la creazione di differenze DOM è un po 'errato, perché sono cose diverse e puoi effettivamente combinarle.

Qual è la differenza allora. Con il controllo sporco è necessario controllare tutto su ogni cambiamento o evento. Potrebbe essere molto performante e Angular2 + lo dimostra. Tuttavia, React d'altra parte sa esattamente quale componente è stato modificato, perché si attiva setState (). Quindi, invece di controllare tutto nella tua app, cercherà semplicemente di costruire il diff dell'albero dei componenti corrente.

Quindi, in conclusione, il controllo sporco non è male, dipende dall'implementazione e dalla tua app. Potresti scoprire che nella tua applicazione è più veloce controllare i valori di controllo che compilare i diff, o viceversa. Qui è un interessante confronto delle prestazioni di React / View / Angular5

    
risposta data 09.06.2018 - 15:57
fonte

Leggi altre domande sui tag