Vue.js Leistungsoptimierung N Tipps (es lohnt sich, sie zu sammeln)

Vue.js Leistungsoptimierung N Tipps (es lohnt sich, sie zu sammeln)

Dieser Artikel bezieht sich hauptsächlich auf das Thema, das Guillaume Chau, ein Kernmitglied von Vue.js, auf der 19. Vue-Konferenz in den USA vorstellte: „9 enthüllte Leistungsgeheimnisse“, in dem neun Techniken zur Leistungsoptimierung von Vue.js erwähnt wurden.

Nachdem ich mir seine PPT-Präsentation angesehen hatte, las ich auch den entsprechenden Quellcode des Projekts. Nachdem ich ein tiefes Verständnis für die Optimierungsprinzipien gewonnen hatte, wandte ich einige der Optimierungstechniken in meiner täglichen Arbeit an und erzielte recht gute Ergebnisse.

Dieses Teilen ist sehr praktisch, aber nicht viele Leute scheinen es zu kennen oder darauf zu achten. Bisher hat das Projekt nur ein paar hundert Sterne. Obwohl es zwei Jahre her ist, seit der Meister seine Erkenntnisse geteilt hat, sind die Optimierungstechniken nicht veraltet. Damit mehr Menschen die praktischen Fähigkeiten verstehen und erlernen können, habe ich beschlossen, seine Erkenntnisse weiter zu verarbeiten, die Optimierungsprinzipien näher zu erläutern und einige Erweiterungen und Erweiterungen vorzunehmen.

Dieser Artikel bezieht sich hauptsächlich auf die Version Vue.js 2.x. Schließlich wird Vue.js 2.x noch einige Zeit die Mainstream-Version in unserer Arbeit sein.

Ich schlage vor, dass Sie den Quellcode des Projekts abrufen und ihn lokal ausführen, wenn Sie diesen Artikel studieren, um den Unterschied in den Effekten vor und nach der Optimierung zu sehen.

Funktionale Komponenten

Die erste Technik, funktionale Komponenten, können Sie sich in diesem Live-Beispiel ansehen

Der Komponentencode vor der Optimierung lautet wie folgt:

<Vorlage>
  <div Klasse="Zelle">
    <div v-if="Wert" Klasse="ein"></div>
    <Abschnitt v-else class="aus"></Abschnitt>
  </div>
</Vorlage>

<Skript>
Standard exportieren {
  Requisiten: ['Wert'],
}
</Skript>

Der optimierte Komponentencode lautet wie folgt:

<funktionale Vorlage>
  <div Klasse="Zelle">
    <div v-if="props.value" Klasse="ein"></div>
    <Abschnitt v-else class="aus"></Abschnitt>
  </div>
</Vorlage>

Anschließend haben wir 800 Komponenten vor und nach der Optimierung der übergeordneten Komponente gerendert und die Aktualisierung der Komponenten durch Ändern der Daten in jedem Frame ausgelöst. Wir haben das Chrome Performance-Bedienfeld geöffnet, um ihre Leistung aufzuzeichnen, und die folgenden Ergebnisse erhalten.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir sehen, dass script vor der Optimierung länger ist als nach der Optimierung. Wie wir wissen, ist die JS-Engine ein Single-Thread-Betriebsmechanismus, und der JS-Thread blockiert den UI-Thread. Wenn die Ausführungszeit des Skripts zu lang ist, wird daher das Rendering blockiert und die Seite friert ein. Das optimierte script hat eine kürzere Ausführungszeit und daher eine bessere Leistung.

Warum verkürzt sich also die Ausführungszeit von JS bei Verwendung funktionaler Komponenten? Dies beginnt mit dem Implementierungsprinzip funktionaler Komponenten. Sie können es sich als eine Funktion vorstellen, die basierend auf den von Ihnen übergebenen Kontextdaten ein DOM rendern und generieren kann.

Funktionale Komponenten unterscheiden sich von gewöhnlichen Objekttypkomponenten. Sie werden nicht als echte Komponenten angesehen. Wir wissen, dass während patch -Prozesses, wenn ein Knoten ein Komponenten- vnode ist, der Initialisierungsprozess der Unterkomponente rekursiv ausgeführt wird; während render von funktionalen Komponenten gewöhnliche vnode generiert und es keinen rekursiven Unterkomponentenprozess gibt, sodass die Renderkosten viel geringer sind.

Daher verfügen funktionale Komponenten nicht über Zustände, Reaktionsdaten, Lebenszyklus-Hook-Funktionen usw. Sie können es sich so vorstellen, als ob ein Teil des DOM aus der normalen Komponentenvorlage entfernt und über eine Funktion gerendert wird. Es handelt sich um eine Art Wiederverwendung auf DOM-Ebene.

Aufteilung untergeordneter Komponenten

Die zweite Technik, die Aufteilung von Unterkomponenten, können Sie sich in diesem Onlinebeispiel ansehen.

Der Komponentencode vor der Optimierung lautet wie folgt:

<Vorlage>
  <div :style="{ Deckkraft: Zahl / 300 }">
    <div>{{ schwer() }}</div>
  </div>
</Vorlage>

<Skript>
Standard exportieren {
  Requisiten: ['Zahl'],
  Methoden: {
    schwer () {
      Konstante n = 100000
      let result = 0
      für (sei i = 0; i < n; i++) {
        Ergebnis += Math.sqrt(Math.cos(Math.sin(42)))
      }
      Ergebnis zurückgeben
    }
  }
}
</Skript>

Der optimierte Komponentencode lautet wie folgt:

<Vorlage>
  <div :style="{ Deckkraft: Zahl / 300 }">
    <UntergeordneteKomponente/>
  </div>
</Vorlage>

<Skript>
Standard exportieren {
  Komponenten:
    Kinderkompatibilität: {
      Methoden: {
        schwer () {
          Konstante n = 100000
          let result = 0
          für (sei i = 0; i < n; i++) {
            Ergebnis += Math.sqrt(Math.cos(Math.sin(42)))
          }
          Ergebnis zurückgeben
        },
      },
      rendern (h) {
        gibt h zurück('div', this.heavy())
      }
    }
  },
  Requisiten: ['Zahl']
}
</Skript>

Dann rendern wir 300 Komponenten vor und nach der Optimierung der übergeordneten Komponente und lösen die Aktualisierung der Komponenten aus, indem wir die Daten in jedem Frame ändern. Wir öffnen das Chrome Performance-Bedienfeld, um ihre Leistung aufzuzeichnen und erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir erkennen, dass die Ausführungszeit script nach der Optimierung erheblich kürzer ist als vor der Optimierung, sodass die Leistung besser ist.

Warum gibt es also einen Unterschied? Schauen wir uns die Komponenten vor der Optimierung an. Das Beispiel simuliert eine zeitaufwändige Aufgabe durch eine heavy Funktion, und diese Funktion wird einmal bei jedem Rendering ausgeführt, sodass das Rendern jeder Komponente viel Zeit in Anspruch nimmt, um JavaScript auszuführen.

Die optimale Methode besteht darin, die Ausführungslogik dieser zeitaufwändigen, heavy Funktion mit der untergeordneten Komponente ChildComp zu kapseln. Da Vue auf Komponentengranularität aktualisiert wird, wird ChildComp nicht erneut gerendert, da darin keine reaktionsfähige Datenänderung vorliegt, obwohl jeder Frame dazu führt, dass die übergeordnete Komponente durch Datenänderung erneut gerendert wird. Daher führen die optimierten Komponenten bei jedem Rendering keine zeitaufwändigen Aufgaben aus, sodass die JavaScript-Ausführungszeit natürlich reduziert wird.

Ich bin jedoch anderer Meinung zu dieser Optimierungsmethode. Für Einzelheiten klicken Sie bitte auf dieses Problem. Ich denke, dass die Verwendung berechneter Eigenschaften zur Optimierung in diesem Szenario besser ist als die Aufteilung in Unterkomponenten. Dank der Caching-Funktion der berechneten Eigenschaften selbst wird zeitaufwändige Logik nur während des ersten Renderings ausgeführt und es entsteht kein zusätzlicher Overhead durch das Rendering von Unterkomponenten bei der Verwendung berechneter Eigenschaften.

In der tatsächlichen Arbeit gibt es viele Szenarien, in denen berechnete Eigenschaften zur Leistungsoptimierung verwendet werden. Schließlich verkörpert es auch die Optimierungsidee des Austauschs von Raum gegen Zeit.

Lokale Variablen

Für den dritten Trick, lokale Variablen, können Sie sich dieses Onlinebeispiel ansehen.

Der Komponentencode vor der Optimierung lautet wie folgt:

<Vorlage>
  <div :style="{ Deckkraft: Start / 300 }">{{ Ergebnis }}</div>
</Vorlage>

<Skript>
Standard exportieren {
  Requisiten: ['Start'],
  berechnet: {
    Basis () {
      Rückkehr 42
    },
    Ergebnis () {
      let ergebnis = this.start
      für (sei i = 0; i < 1000; i++) {
        Ergebnis += Math.sqrt(Math.cos(Math.sin(diese.Basis))) + diese.Basis * diese.Basis + diese.Basis + diese.Basis * 2 + diese.Basis * 3
      }
      Ergebnis zurückgeben
    },
  },
}
</Skript>

Der optimierte Komponentencode lautet wie folgt:

<Vorlage>
  <div :style="{ Deckkraft: Start / 300 }">{{ Ergebnis }}</div>
</Vorlage>

<Skript>
Standard exportieren {
  Requisiten: ['Start'],
  berechnet: {
    Basis () {
      Rückkehr 42
    },
    Ergebnis ({ Basis, Start }) {
      let ergebnis = start
      für (sei i = 0; i < 1000; i++) {
        Ergebnis += Math.sqrt(Math.cos(Math.sin(Basis))) + Basis * Basis + Basis + Basis * 2 + Basis * 3
      }
      Ergebnis zurückgeben
    },
  },
}
</Skript>

Dann rendern wir 300 Komponenten vor und nach der Optimierung der übergeordneten Komponente und lösen die Aktualisierung der Komponenten aus, indem wir die Daten in jedem Frame ändern. Wir öffnen das Chrome Performance-Bedienfeld, um ihre Leistung aufzuzeichnen und erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir erkennen, dass die Ausführungszeit script nach der Optimierung erheblich kürzer ist als vor der Optimierung, sodass die Leistung besser ist.

Der Hauptunterschied besteht hier in der Implementierungsdifferenz des berechneten result der Komponenten vor und nach der Optimierung. Die Komponente vor der Optimierung greift während des Berechnungsprozesses viele Male auf this.base zu, während die Komponente nach der Optimierung die lokale Variable base verwendet, um this.base vor der Berechnung zwischenzuspeichern und dann direkt auf base zuzugreifen.

Warum führt dieser Unterschied also zu Leistungsunterschieden? Der Grund dafür ist, dass jedes Mal, wenn Sie auf this.base zugreifen, dessen getter ausgelöst wird, da this.base ein responsives Objekt ist, und dann der mit der Abhängigkeitssammlung verbundene Logikcode ausgeführt wird. Wenn eine ähnliche Logik zu oft ausgeführt wird, wie im Beispiel, wo Hunderte von Komponenten in Hunderten von Zyklen aktualisiert werden, jede Komponente eine Neuberechnung der computed auslöst und dann die mit der Abhängigkeitssammlung verbundene Logik mehrere Male ausgeführt wird, nimmt die Leistung natürlich ab.

Aus Sicht der Nachfrage reicht es aus, wenn this.base einmal eine Abhängigkeitssammlung durchführt, sodass wir nur das Ergebnis seiner getter -Auswertung an die lokale Variable base zurückgeben müssen. Wenn base später erneut aufgerufen wird, wird getter nicht ausgelöst und die Abhängigkeitssammlungslogik wird nicht befolgt, sodass die Leistung natürlich verbessert wird.

Dies ist eine sehr praktische Technik zur Leistungsoptimierung. Denn wenn viele Leute Vue.js-Projekte entwickeln, sind sie es gewohnt, this.xxx direkt zu schreiben, wenn sie Variablen übernehmen, weil die meisten Leute nicht darauf achten, was hinter dem Zugriff auf this.xxx passiert. Bei einer geringen Anzahl von Zugriffen treten keine Leistungsprobleme auf. Sobald die Anzahl der Zugriffe jedoch zunimmt, z. B. bei mehreren Zugriffen in einer großen Schleife, ähnlich wie im Beispielszenario, treten Leistungsprobleme auf.

Als ich die Leistung der Tabellenkomponente von ZoomUI optimierte, verwendete ich beim render table body die Optimierungstechnik lokaler Variablen und schrieb einen Benchmark zum Leistungsvergleich: Beim Rendern einer 1000 x 10-Tabelle war die Leistung des erneuten Renderns aktualisierter Daten der ZoomUI-Tabelle fast doppelt so hoch wie die der ElementUI-Tabelle.

DOM mit v-show wiederverwenden

Der vierte Tipp besteht darin, DOM mit v-show wiederzuverwenden. Sie können sich dieses Onlinebeispiel ansehen.

Der Komponentencode vor der Optimierung lautet wie folgt:

<funktionale Vorlage>
  <div Klasse="Zelle">
    <div v-if="Eigenschaften.Wert" Klasse="ein">
      <Schwer :n="10000"/>
    </div>
    <Abschnitt v-else Klasse="aus">
      <Schwer :n="10000"/>
    </Abschnitt>
  </div>
</Vorlage>

Der optimierte Komponentencode lautet wie folgt:

<funktionale Vorlage>
  <div Klasse="Zelle">
    <div v-show="Eigenschaften.Wert" Klasse="ein">
      <Schwer :n="10000"/>
    </div>
    <Abschnitt v-show="!props.value" Klasse="aus">
      <Schwer :n="10000"/>
    </Abschnitt>
  </div>
</Vorlage>

Dann rendern wir 200 Komponenten vor und nach der Optimierung der übergeordneten Komponente und lösen die Aktualisierung der Komponenten aus, indem wir die Daten in jedem Frame ändern. Wir öffnen das Chrome Performance-Bedienfeld, um ihre Leistung aufzuzeichnen und erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir erkennen, dass die Ausführungszeit script nach der Optimierung erheblich kürzer ist als vor der Optimierung, sodass die Leistung besser ist.

Der Hauptunterschied vor und nach der Optimierung besteht darin, dass die Direktive v-show anstelle der Direktive v-if verwendet wird, um die Sichtbarkeit von Komponenten zu ersetzen. Obwohl v-show und v-if in der Leistung ähnlich sind und beide die Sichtbarkeit von Komponenten steuern, besteht bei ihrer internen Implementierung immer noch eine große Lücke.

v-if Direktive wird während der Kompilierungsphase in einen ternären Operator kompiliert, der für bedingtes Rendering verwendet wird. Beispielsweise wird die Komponentenvorlage vor der Optimierung kompiliert, um die folgende Rendering-Funktion zu generieren:

Funktion rendern() {
  mit(diesem) {
    return _c('div', {
      statische Klasse: "Zelle"
    }, [(Eigenschaften.Wert) ? _c('div', {
      statische Klasse: „ein“
    }, [_c('Schwer', {
      Attribute: {
        "n": 10000
      }
    })], 1) : _c('Abschnitt', {
      statische Klasse: "aus"
    }, [_c('Schwer', {
      Attribute: {
        "n": 10000
      }
    })], 1)])
  }
}

Wenn sich der Wert der Bedingung props.value ändert, wird die entsprechende Komponentenaktualisierung ausgelöst. Da vnode der alten und neuen Knoten für die von v-if gerenderten Knoten inkonsistent sind, werden die alten vnode -Knoten entfernt und während des Vergleichsprozesses des Core-Diff-Algorithmus neue vnode -Knoten erstellt. Anschließend wird Heavy neue Heavy Komponente erstellt, die den Prozess der Initialisierung, des Renderns vnode , patch usw. durchläuft.

Daher wird bei Verwendung von v-if bei jeder Aktualisierung einer Komponente eine neue Heavy Unterkomponente erstellt. Wenn mehrere Komponenten aktualisiert werden, führt dies natürlich zu Leistungseinbußen.

Wenn wir die v-show Direktive verwenden, wird die optimierte Komponentenvorlage kompiliert, um die folgende Rendering-Funktion zu generieren:

Funktion rendern() {
  mit(diesem) {
    return _c('div', {
      statische Klasse: "Zelle"
    }, [_c('div', {
      Anweisungen: [{
        Name: "show",
        Rohname: "v-show",
        Wert: (Eigenschaften.Wert),
        Ausdruck: "props.value"
      }],
      statische Klasse: „ein“
    }, [_c('Schwer', {
      Attribute: {
        "n": 10000
      }
    })], 1), _c('Abschnitt', {
      Anweisungen: [{
        Name: "show",
        Rohname: "v-show",
        Wert: (!props.value),
        Ausdruck: "!props.value"
      }],
      statische Klasse: "aus"
    }, [_c('Schwer', {
      Attribute: {
        "n": 10000
      }
    })], 1)])
  }
}

Wenn sich der Wert der Bedingung props.value ändert, wird die entsprechende Komponentenaktualisierung ausgelöst. Für die von v-show gerenderten Knoten gilt: Da die alten und neuen vnode gleich sind, müssen sie nur patchVnode . Wie werden also DOM-Knoten angezeigt und ausgeblendet?

Es stellt sich heraus, dass während des patchVnode Prozesses die Hook-Funktion, die v-show Anweisung entspricht, intern update und dann den style.display -Wert des DOM-Elements, auf das sie einwirkt, entsprechend dem an v-show Anweisung gebundenen Wert einstellt, um die Sichtbarkeit zu steuern.

Im Vergleich zu v-if das ständig DOM löscht und neues erstellt, aktualisiert v-show nur die Sichtbarkeit des vorhandenen DOM. Daher ist der Overhead von v-show viel geringer als der von v-if . Je komplexer die interne DOM-Struktur, desto größer der Leistungsunterschied.

Der Leistungsvorteil von v-show gegenüber v-if liegt jedoch in der Komponentenaktualisierungsphase. Wenn es nur in der Initialisierungsphase ist, ist die Leistung von v-if höher als die v-show . Der Grund dafür ist, dass nur ein Zweig gerendert wird, während v-show beide Zweige rendert und die Sichtbarkeit des entsprechenden DOM über style.display steuert.

Bei Verwendung von v-show werden alle Komponenten innerhalb der Zweige gerendert und die entsprechenden Lebenszyklus-Hook-Funktionen ausgeführt. Bei Verwendung von v-if werden Komponenten innerhalb der Zweige, die nicht getroffen werden, nicht gerendert und die entsprechenden Lebenszyklus-Hook-Funktionen nicht ausgeführt.

Daher müssen Sie ihre Prinzipien und Unterschiede verstehen, damit Sie in verschiedenen Szenarien entsprechende Anweisungen verwenden können.

Am Leben bleiben

Der fünfte Tipp besteht darin, die KeepAlive Komponente zum Zwischenspeichern des DOM zu verwenden. Sie können sich dieses Onlinebeispiel ansehen.

Der Komponentencode vor der Optimierung lautet wie folgt:

<Vorlage>
  <div id="app">
    <Router-Ansicht/>
  </div>
</Vorlage>

Der optimierte Komponentencode lautet wie folgt:

<Vorlage>
  <div id="app">
    <am Leben erhalten>
      <Router-Ansicht/>
    </am Leben erhalten>
  </div>
</Vorlage>

Wenn wir auf die Schaltfläche klicken, um zwischen der einfachen Seite und der umfangreichen Seite zu wechseln, werden unterschiedliche Ansichten gerendert und das Rendern der umfangreichen Seite ist sehr zeitaufwändig. Wir öffnen das Leistungsfenster von Chrome, um die Leistung aufzuzeichnen, und führen dann die oben genannten Vorgänge vor und nach der Optimierung durch. Dabei erhalten wir die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir erkennen, dass die Ausführungszeit script nach der Optimierung erheblich kürzer ist als vor der Optimierung, sodass die Leistung besser ist.

In einem nicht optimierten Szenario wird die Komponente jedes Mal neu gerendert, wenn wir auf eine Schaltfläche klicken, um die Routenansicht zu wechseln. Die gerenderte Komponente durchläuft die Komponenteninitialisierung, render , patch und andere Prozesse. Wenn die Komponente komplex oder tief verschachtelt ist, dauert das gesamte Rendering lange.

Nach der Verwendung KeepAlive werden vnode und der DOM der von KeepAlive umschlossenen Komponente nach dem ersten Rendern zwischengespeichert. Wenn die Komponente dann das nächste Mal erneut gerendert wird, werden der entsprechende vnode und der DOM direkt aus dem Cache abgerufen und dann gerendert. Es ist nicht erforderlich, eine Reihe von Prozessen wie Komponenteninitialisierung, render und erneutes patch zu durchlaufen, was die Ausführungszeit script verkürzt und die Leistung verbessert.

Allerdings ist die Verwendung der KeepAlive Komponente mit Kosten verbunden, da sie mehr Speicher für die Zwischenspeicherung beansprucht, was eine typische Anwendung des Konzepts der Raum-Zeit-Optimierung ist.

Zurückgestellte Features

Der sechste Tipp besteht darin, Deferred zu verwenden, um das Rendern von Komponenten in Stapeln zu verzögern. Sie können sich dieses Onlinebeispiel ansehen.

Der Komponentencode vor der Optimierung lautet wie folgt:

<Vorlage>
  <div Klasse="aufgeschoben-aus">
    <VueIcon icon="fitness_center" class="gigantisch"/>

    <h2>Ich bin eine schwere Seite</h2>

    <Schweres v-for="n in 8" :key="n"/>

    <Schwere Klasse="superschwer" :n="9999999"/>
  </div>
</Vorlage>

Der optimierte Komponentencode lautet wie folgt:

<Vorlage>
  <div Klasse="aufgeschoben-am">
    <VueIcon icon="fitness_center" class="gigantisch"/>

    <h2>Ich bin eine schwere Seite</h2>

    <template v-if="aufschieben(2)">
      <Schweres v-for="n in 8" :key="n"/>
    </Vorlage>

    <Schwer v-if="defer(3)" class="super-schwer" :n="9999999"/>
  </div>
</Vorlage>

<Skript>
importiere Defer von '@/mixins/Defer'

Standard exportieren {
  Mixins:
    Verschieben(),
  ],
}
</Skript>

Wenn wir auf die Schaltfläche klicken, um zwischen der einfachen Seite und der umfangreichen Seite zu wechseln, werden unterschiedliche Ansichten gerendert und das Rendern der umfangreichen Seite ist sehr zeitaufwändig. Wir öffnen das Leistungsfenster von Chrome, um die Leistung aufzuzeichnen, und führen dann die oben genannten Vorgänge vor und nach der Optimierung durch. Dabei erhalten wir die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Durch Vergleich dieser beiden Bilder können wir feststellen, dass vor der Optimierung, wenn wir von einer einfachen Seite zu einer umfangreichen Seite wechseln und sich ein Rendering dem Ende nähert, die Seite immer noch als einfache Seite gerendert wird, was den Eindruck einer Seitenverzögerung vermittelt. Wenn wir nach der Optimierung von der einfachen Seite zur umfangreichen Seite wechseln, wird die umfangreiche Seite bereits in einem Rendervorgang am Anfang der Seite gerendert und die umfangreiche Seite wird schrittweise gerendert.

Der Unterschied zwischen der Optimierung vor und nach der Optimierung besteht hauptsächlich darin, dass bei letzterer das Defer mixin verwendet wird. Schauen wir uns an, wie es funktioniert:

Standardfunktion exportieren (Anzahl = 10) {
  zurückkehren {
    Daten () {
      zurückkehren {
        Anzeigepriorität: 0
      }
    },

    montiert () {
      dies.runDisplayPriority()
    },

    Methoden: {
      runDisplayPriority() {
        const Schritt = () => {
          requestAnimationFrame(() => {
            diese.displayPriority++
            wenn (this.displayPriority < Anzahl) {
              Schritt()
            }
          })
        }
        Schritt()
      },

      aufschieben (Priorität) {
        returniere this.displayPriority >= Priorität
      }
    }
  }
}

Die Grundidee von Defer besteht darin, das Rendern einer Komponente in mehrere Zeiträume aufzuteilen. Dabei wird die Variable displayPriority intern verwaltet und dann bei jedem Frame-Rendering über requestAnimationFrame bis auf count erhöht. Dann können Sie innerhalb der Komponente, die Defer mixin verwendet, v-if="defer(xxx)" verwenden, um die Darstellung bestimmter Blöcke zu steuern, wenn displayPriority auf xxx ansteigt.

Wenn Sie Komponenten haben, deren Rendern Zeit in Anspruch nimmt, empfiehlt es sich, Deferred für progressives Rendering zu verwenden. Dadurch kann das Phänomen vermieden werden, dass render aufgrund einer langen JS-Ausführungszeit hängen bleibt.

Zeitscheiben

Der siebte Tipp ist die Verwendung Time slicing -Slicing-Technologie. Sie können sich dieses Online-Beispiel ansehen.

Der Code vor der Optimierung sieht wie folgt aus:

fetchItems ({ commit }, { items }) {
  commit('Elemente löschen')
  commit('Artikel hinzufügen', Artikel)
}

Der optimierte Code lautet wie folgt:

fetchItems ({ commit }, { items, splitCount }) {
  commit('Elemente löschen')
  const queue = neue JobQueue()
  splitArray(Elemente, splitCount).fürJeden(
    chunk => queue.addJob(erledigt => {
      // Daten in Zeitscheiben übermitteln requestAnimationFrame(() => {
        commit('Elemente hinzufügen', Block)
        Erledigt()
      })
    })
  )
  warte auf Warteschlange.start()
}

Wir erstellen zunächst 10.000 gefälschte Daten, indem wir auf Genterate items klicken, und klicken dann auf Commit items , um die Daten Time-slicing zu übermitteln. Wir öffnen das Chrome Performance-Bedienfeld, um ihre Leistung aufzuzeichnen und erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Durch den Vergleich dieser beiden Bilder können wir feststellen, dass die gesamte script vor der Optimierung kürzer ist als nach der Optimierung. Aus der tatsächlichen visuellen Erfahrung geht jedoch hervor, dass die Seite beim Klicken auf die Schaltfläche „Senden“ vor der Optimierung etwa 1,2 Sekunden lang hängen bleibt. Nach der Optimierung bleibt die Seite nicht vollständig hängen, aber es wird immer noch das Gefühl einer Verzögerung beim Rendern auftreten.

Warum friert die Seite vor der Optimierung ein? Da zu viele Daten auf einmal übermittelt wurden, war die interne JS-Ausführungszeit zu lang, wodurch der UI-Thread blockiert wurde und die Seite einfror.

Nach der Optimierung weist die Seite immer noch einige Verzögerungen auf, da wir die Daten mit einer Granularität von 1.000 Elementen aufgeteilt haben. In diesem Fall besteht immer noch Druck, die Komponenten neu zu rendern. Wir haben festgestellt, dass die fps nur etwa ein Dutzend betrugen, was einige Verzögerungen verursachte. Normalerweise läuft die Seite sehr flüssig, solange die fps der Seite 60 erreichen. Wenn wir die Daten in 100 Teile aufteilen, können die fps grundsätzlich mehr als 50 erreichen. Obwohl das Rendern der Seite flüssiger wird, ist die Gesamtübermittlungszeit für die Fertigstellung von 10.000 Datenteilen immer noch länger.

Mithilfe Time slicing -Technologie können Seiteneinfrierungen vermieden werden. Normalerweise fügen wir bei der Verarbeitung solcher zeitaufwändiger Aufgaben einen loading hinzu. In diesem Beispiel können wir loading animation einschalten und dann die Daten übermitteln. Im Vergleich dazu haben wir festgestellt, dass JS vor der Optimierung zu lange ausgeführt wurde, weil zu viele Daten gleichzeitig übermittelt wurden. Dadurch wurde der UI-Thread blockiert und die loading wurde nicht angezeigt. Nach der Optimierung wurde die einzelne JS-Ausführungszeit kürzer, da wir die Daten in mehrere Zeitscheiben aufgeteilt haben, sodass loading angezeigt werden konnte.

Hier ist zu beachten, dass wir zwar requestAnimationFrame -API verwenden, um den Zeitabschnitt aufzuteilen, die Verwendung requestAnimationFrame selbst jedoch keinen Vollbildbetrieb garantieren kann. requestAnimationFrame garantiert, dass die entsprechende übergebene Rückruffunktion nach jeder Neuzeichnung durch den Browser ausgeführt wird. Um einen Vollbildbetrieb sicherzustellen, besteht die einzige Möglichkeit darin, JS nicht länger als 17 ms pro Tick laufen zu lassen.

Nichtreaktive Daten

Der achte Tipp besteht darin, Non-reactive data zu verwenden. Sie können sich dieses Onlinebeispiel ansehen.

Der Code vor der Optimierung sieht wie folgt aus:

const Daten = Elemente.Map(
  Artikel => ({
    Ich würde sagen,
    Daten: Artikel,
    Abstimmung: 0
  })
)

Der optimierte Code lautet wie folgt:

const Daten = Elemente.Map(
  Element => OptimierenElement(Element)
)

Funktion optimizeItem (Element) {
  const itemData = {
    Ich würde sagen,
    Abstimmung: 0
  }
  Objekt.defineProperty(itemData, 'data', {
    // Als nicht reaktiv markieren
    konfigurierbar: false,
    Wert: Artikel
  })
  itemData zurückgeben
}

Wir verwenden immer noch das vorherige Beispiel und erstellen zunächst 10.000 gefälschte Daten, indem wir auf Genterate items klicken. Klicken Sie dann auf Commit items um die Daten mit jeweils ein- und ausgeschalteter Partial reactivity zu übermitteln. Wir öffnen das Leistungsfenster von Chrome, um ihre Leistung aufzuzeichnen, und erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder können wir erkennen, dass die Ausführungszeit script nach der Optimierung erheblich kürzer ist als vor der Optimierung, sodass die Leistung besser ist.

Der Grund für diesen Unterschied liegt darin, dass bei interner Datenübermittlung die neu übermittelten Daten standardmäßig als responsiv definiert werden. Wenn die Unterattribute der Daten in Objektform vorliegen, werden die Unterattribute ebenfalls rekursiv responsiv gemacht. Daher wird dieser Vorgang bei der Übermittlung großer Datenmengen zeitaufwändig.

Nach der Optimierung haben wir die data in den neu übermittelten Daten manuell configurable auf false geändert. Auf diese Weise werden data ignoriert, wenn das Objektattributarray während walk über Object.keys(obj) abgerufen wird, und defineReactive wird nicht für das data festgelegt. Da data auf ein Objekt verweist, wird dadurch die rekursive Reaktionslogik reduziert, was einer Reduzierung des Leistungsverlusts dieses Teils entspricht. Der Effekt dieser Optimierung ist umso deutlicher, je größer die Datenmenge ist.

Tatsächlich gibt es viele ähnliche Optimierungsmethoden. Beispielsweise müssen einige Daten, die wir in der Komponente definieren, nicht unbedingt in data definiert sein. Einige Daten werden in der Vorlage nicht verwendet und wir müssen auch ihre Änderungen nicht überwachen. Wir möchten diese Daten nur im Kontext der Komponente freigeben. Zu diesem Zeitpunkt können wir diese Daten einfach in die Komponenteninstanz this einbinden, zum Beispiel:

Standard exportieren {
  erstellt() {
    this.scroll = null
  },
  montiert() {
    dies.scroll = neues BScroll(dies.$el)
  }
}

Auf diese Weise können wir scroll Objekt im Komponentenkontext teilen, obwohl es kein responsives Objekt ist.

Virtuelles Scrollen

Der neunte Tipp besteht darin, Virtual scrolling zu verwenden. Sie können sich dieses Onlinebeispiel ansehen.

Der Code der Komponente vor der Optimierung lautet wie folgt:

<div Klasse="Artikel no-v">
  <FetchItemViewFunktional
    v-for="Artikel von Artikeln"
    :Schlüssel="Artikel-ID"
    :Artikel="Artikel"
    @vote="voteItem(Artikel)"
  />
</div>

Der optimierte Code lautet wie folgt:

<Recycling-Scroller
  Klasse="Artikel"
  :items="Artikel"
  :Artikelgröße="24"
>
  <template v-slot="{ Element }">
    <Artikelansicht abrufen
      :Artikel="Artikel"
      @vote="voteItem(Artikel)"
    />
  </Vorlage>
</recycle-scroller>

Wir verwenden immer noch das vorherige Beispiel und müssen View list öffnen und dann auf Genterate items klicken, um 10.000 gefälschte Daten zu erstellen (beachten Sie, dass das Onlinebeispiel höchstens 1.000 Daten erstellen kann. Tatsächlich können 1.000 Daten den Optimierungseffekt nicht gut widerspiegeln, daher habe ich die Quellcodebeschränkung geändert, ihn lokal ausgeführt und 10.000 Daten erstellt). Dann müssen wir in Unoptimized und RecycleScroller auf Commit items klicken, um die Daten zu übermitteln, durch die Seite zu scrollen und das Leistungsfenster von Chrome zu öffnen, um ihre Leistung aufzuzeichnen. Sie erhalten die folgenden Ergebnisse.

Vor der Optimierung:

Nach der Optimierung:

Beim Vergleich dieser beiden Bilder stellen wir fest, dass im nicht optimierten Fall die fps von 10.000 Daten beim Scrollen nur einstellig sind und beim Nicht-Scrollen nur im Dutzend. Der Grund dafür ist, dass im nicht optimierten Szenario zu viele DOMs gerendert werden und das Rendering selbst einem großen Druck ausgesetzt ist. Nach der Optimierung können die fps sogar bei 10.000 Datenelementen im Scroll-Fall über 30 und im Nicht-Scroll-Fall 60 Vollbilder erreichen.

Der Grund für diesen Unterschied liegt in der Art und Weise, wie virtuelles Scrollen implementiert wird: Es rendert nur das DOM innerhalb des Ansichtsfensters. Auf diese Weise ist die Gesamtzahl der gerenderten DOMs sehr gering und die Leistung natürlich viel besser.

Die virtuelle Scroll-Komponente wurde ebenfalls von Guillaume Chau geschrieben. Interessierte Studierende können sich die Implementierung im Quellcode ansehen. Sein Grundprinzip besteht darin, auf Bildlaufereignisse zu achten, die anzuzeigenden DOM-Elemente dynamisch zu aktualisieren und ihre Verschiebung in der Ansicht zu berechnen.

Die virtuelle Bildlaufkomponente ist nicht kostenlos, da sie während des Bildlaufvorgangs in Echtzeit berechnet werden muss, sodass bestimmte Kosten für script anfallen. Wenn die Datenmenge in der Liste nicht sehr groß ist, reicht daher die Verwendung des normalen Scrollens aus.

Zusammenfassen

Ich hoffe, dass Sie durch diesen Artikel neun Techniken zur Leistungsoptimierung für Vue.js erlernen und diese in tatsächlichen Entwicklungsprojekten anwenden können. Zusätzlich zu den oben genannten Techniken gibt es auch häufig verwendete Methoden zur Leistungsoptimierung, wie z. B. das verzögerte Laden von Bildern, verzögerte Laden von Komponenten, asynchrone Komponenten usw.

Bevor wir die Leistung optimieren, müssen wir analysieren, wo der Leistungsengpass liegt, damit wir entsprechende Maßnahmen ergreifen können. Darüber hinaus erfordert die Leistungsoptimierung Datenunterstützung. Bevor Sie eine Leistungsoptimierung durchführen, müssen Sie vor der Optimierung Daten sammeln, damit Sie den Optimierungseffekt durch Datenvergleich nach der Optimierung erkennen können.

Ich hoffe, dass Sie sich bei zukünftigen Entwicklungen nicht mehr mit der bloßen Erfüllung von Anforderungen zufrieden geben, sondern beim Schreiben jeder Codezeile auch die möglichen Auswirkungen auf die Leistung berücksichtigen.

Verweise

[1] vue-9-perf-secrets-Folie: https://slides.com/akryum/vueconfus-2019

[2] gemeinsames Redevideo zu vue-9-perf-secrets: https://www.vuemastery.com/conference/vueconf-us-2019/9-performance-secrets-revealed/

[3] Quellcode des Projekts vue-9-perf-secrets: https://github.com/Akryum/vue-9-perf-secrets

[4] vue-9-perf-secrets Online-Demo-Adresse: https://vue-9-perf-secrets.netlify.app/

[5] Diskussionsthema zu vue-9-perf-secrets: https://github.com/Akryum/vue-9-perf-secrets/issues/1

[6] Quellcode des vue-virtual-scroller-Projekts: https://github.com/Akryum/vue-virtual-scroller

Damit ist dieser Artikel über neun Tipps zur Leistungsoptimierung für Vue.js (es lohnt sich, ihn zu sammeln) abgeschlossen. Weitere relevante Tipps zur Leistungsoptimierung von Vue.js finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Eine kurze Diskussion zur Leistungsoptimierung der Vue-Initialisierung
  • Eine kurze Diskussion zur Leistungsoptimierung von Vue: Ein tiefer Einblick in Arrays
  • Methoden zur Optimierung der Vue-Leistung
  • Zwölf Tipps zur Leistungsoptimierung für die Vue-Entwicklung

<<:  So prüfen Sie, ob ein Port in LINUX belegt ist

>>:  Eine kurze Diskussion über die MySQL-Zeilenanzahl

Artikel empfehlen

Beispielcode zur Implementierung eines einfachen ListViews-Effekts in HTML

HTML zum Erreichen eines einfachen ListViews-Effe...

25 CSS-Frameworks, Tools, Software und Vorlagen geteilt

Kobold-Kuh herunterladen CSS-Fussel herunterladen...

mysql: [FEHLER] unbekannte Option '--skip-grant-tables'

MySQL-Datenbank meldet FEHLER 1045 (28000): Zugri...

JavaScript zum Erzielen eines Texterweiterungs- und -reduzierungseffekts

Die Implementierung des Erweiterns und Reduzieren...

Beispielcode zur Implementierung der Menüberechtigungssteuerung in Vue

Wenn Benutzer an einem Backend-Verwaltungssystem ...

Detailliertes Installationstutorial für mysql-8.0.11-winx64.zip

Laden Sie das ZIP-Installationspaket herunter: Do...

25 Beispiele für Website-Design im Nachrichtenstil

bmi Voyager Heugabel Ulster Lebensmittelhändler F...

So erstellen Sie geplante Aufgaben mit dem Crond-Tool in Linux

Vorwort Crond ist ein Tool zur geplanten Ausführu...

Verwenden Sie das Firebug-Tool, um die Seite auf dem iPad zu debuggen

Wie debuggt man eine Seite auf dem iPad? Wenn Sie ...

Detaillierte Erklärung der Schritte zum Erstellen eines Vue-Projekts mit Vue-cli

Zuerst müssen Sie Vue-cli installieren: npm insta...