9 Tipps zur Vue-Leistung

Dieser Artikel enthält neun Tipps zum Verbessern der Leistung Ihrer Vue-Anwendung, zum Erhöhen der Anzeigegeschwindigkeit und zum Verringern der Paketgröße.

Arbeiten Sie mit Komponenten


Funktionskomponenten


Angenommen, wir haben eine einfache, kleine Komponente. Er zeigt lediglich ein bestimmtes Tag an, abhängig vom übergebenen Wert:

<template> <div> <div v-if="value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script> 

Diese Komponente kann durch Hinzufügen des Funktionsattributs optimiert werden. Eine Funktionskomponente wird zu einer einfachen Funktion kompiliert und hat keinen lokalen Status. Aufgrund dessen ist seine Leistung viel höher:

 <template functional> <div> <div v-if="props.value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script> 

Ein Beispiel dafür, wie Funktionskomponenten in Vue v3.0 aussehen könnten

Trennung in untergeordnete Komponenten


Stellen Sie sich eine anzuzeigende Komponente vor, die Sie für komplexe Aufgaben benötigen:

 <template> <div :style="{ opacity: number / 300 }"> <div>{{ heavy() }}</div> </div> </template> <script> export default { props: ['number'], methods: { heavy () { /*   */ } } } </script> 

Das Problem hierbei ist, dass Vue die heavy () -Methode jedes Mal ausführt, wenn die Komponente neu gerendert wird, dh jedes Mal, wenn sich der Requisitenwert ändert.

Wir können eine solche Komponente leicht optimieren, wenn wir die schwere Methode in eine untergeordnete Komponente aufteilen:

 <template> <div :style="{ opacity: number / 300 }"> <ChildComp/> </div> </template> <script> export default { props: ['number'], components: { ChildComp: { methods: { heavy () { /*   */ } }, render (h) { return h('div', this.heavy()) } } } } </script> 

Warum? Vue überspringt automatisch das Rendern von Komponenten, in denen sich abhängige Daten nicht geändert haben. Wenn also Requisiten in der Elternkomponente geändert werden, wird das Kind wiederverwendet und die heavy () -Methode wird nicht neu gestartet.

Beachten Sie, dass dies nur dann sinnvoll ist, wenn die untergeordnete Komponente keine Datenabhängigkeit in der übergeordneten Komponente aufweist. Andernfalls wird das untergeordnete Element zusammen mit dem übergeordneten Element neu erstellt. Diese Optimierung ist dann nicht sinnvoll.

Getter Local Cache


Die folgende Komponente verfügt über eine berechnete Eigenschaft, die auf der zweiten berechneten Eigenschaft basiert:

 <template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { let result = this.start for (let i = 0; i < 1000; i++) { result += this.base } return result } } } </script> 

Wichtig ist hierbei, dass die Base-Eigenschaft in einer Schleife aufgerufen wird, was zu Komplikationen führt. Bei jedem Zugriff auf reaktive Daten führt Vue eine Logik aus, um zu bestimmen, wie und auf welche Daten Sie zugreifen, um Abhängigkeiten usw. aufzubauen. Diese kleinen Gemeinkosten werden summiert, wenn viele Anrufe vorliegen, wie in unserem Beispiel.

Um dies zu beheben, greifen Sie einfach einmal auf base zu und speichern den Wert in einer lokalen Variablen:

 <template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { const base = this.base // <-- let result = this.start for (let i = 0; i < 1000; i++) { result += base } return result } } } </script> 

Wiederverwenden von DOM mit V-Show


Schauen Sie sich folgendes Beispiel an:

 <template functional> <div> <div v-if="props.value"> <Heavy :n="10000"/> </div> <section v-else> <Heavy :n="10000"/> </section> </div> </template> 

Hier haben wir eine Wrapper-Komponente, die v-if und v-else verwendet, um einige untergeordnete Komponenten zu wechseln.

Es ist wichtig zu verstehen, wie v-if funktioniert. Jedes Mal, wenn ein Status gewechselt wird, wird eine untergeordnete Komponente vollständig zerstört (der destroy () - Hook wird aufgerufen, alle Knoten werden aus dem DOM gelöscht), und die zweite wird vollständig erstellt und erneut bereitgestellt. Und wenn diese Komponenten „schwer“ sind, hängt die Schnittstelle beim Umschalten.

Produktivere Lösung - Verwenden Sie V-Show

 <template functional> <div> <div v-show="props.value"> <Heavy :n="10000"/> </div> <section v-show="!props.value"> <Heavy :n="10000"/> </section> </div> </template> 

In diesem Fall werden beide untergeordneten Komponenten sofort erstellt und bereitgestellt und sind gleichzeitig vorhanden. Somit muss Vue beim Umschalten keine Komponenten zerstören und erstellen. Alles, was er tut, ist, eine zu verstecken und die zweite mit CSS anzuzeigen. Das Wechseln des Status ist also viel schneller, aber Sie sollten verstehen, dass dies zu hohen Speicherkosten führt.

Verwenden Sie <Keep-Alive>


Eine einfache Komponente ist also eine Wrapper-Komponente über einem Router .

 <template> <div id="app"> <router-view/> </div> </template> 

Das Problem ähnelt dem vorherigen Beispiel: Alle Komponenten in Ihrem Router werden während der Übergänge zwischen Routen erstellt, bereitgestellt und zerstört.

Und die Lösung hier ist ähnlich - um Vue anzuweisen, Komponenten nicht zu zerstören, sondern zwischenzuspeichern und wiederzuverwenden. Sie können dies mit der speziellen integrierten Komponente <keep-alive> tun:

 <template> <div id="app"> <keep-alive> <router-view/> </keep-alive> </div> </template> 

Diese Optimierung wird zu einem erhöhten Speicherverbrauch führen, da Vue mehr Komponenten "lebendig" unterstützt. Daher sollten Sie diesen Ansatz nicht für alle Routen bedenkenlos anwenden, insbesondere wenn Sie viele Routen haben. Mit den Einschluss- und Ausschlussattributen können Sie festlegen, welche Routen zwischengespeichert werden sollen und welche nicht:

 <template> <div id="app"> <keep-alive include="home,some-popular-page"> <router-view/> </keep-alive> </div> </template> 

Verzögertes Rendern


Das folgende Beispiel ist eine Komponente mit mehreren sehr schweren untergeordneten Komponenten:

 <template> <div> <h3>I'm an heavy page</h3> <Heavy v-for="n in 10" :key="n"/> <Heavy class="super-heavy" :n="9999999"/> </div> </template> 

Das Problem ist, dass die Komponentenverarbeitung eine synchrone Operation im Hauptthread ist. In diesem Beispiel zeigt der Browser erst dann etwas an, wenn die Verarbeitung aller über- und untergeordneten Komponenten abgeschlossen ist. Und wenn die Verarbeitung von Tochterunternehmen viel Zeit in Anspruch nimmt, treten für einige Zeit Verzögerungen in der Benutzeroberfläche oder ein leerer Bildschirm auf.

Sie können die Situation verbessern, indem Sie das Rendern von untergeordneten Komponenten verzögern:

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="defer(2)"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/> </div> </template> <script> import Defer from '@/mixins/Defer' export default { mixins: [ Defer() ] } </script> 

Hier gibt die Funktion defer (n) falsche n Frames zurück. Danach wird immer true zurückgegeben. Es wird verwendet, um die Verarbeitung eines Teils der Vorlage um mehrere Frames zu verschieben, wodurch der Browser die Möglichkeit erhält, die Benutzeroberfläche zu zeichnen.

Wie funktioniert es Wie ich oben geschrieben habe, ignoriert Vue einen Teil der Vorlage vollständig, wenn die Bedingung in der v-if-Direktive falsch ist.

Im ersten Frame der Animation erhalten wir:

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="false"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template> 

Vue hängt einfach den Header ein und der Browser zeigt ihn an und fordert einen zweiten Frame an. An diesem Punkt beginnt defer (2), true zurückzugeben

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="true"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template> 

Vue erstellt 10 untergeordnete Komponenten und stellt sie bereit. Der Browser zeigt sie an und fordert den nächsten Frame an, bei dem defer (3) true zurückgibt.

Aus diesem Grund haben wir eine umfangreiche Komponente erstellt, die schrittweise verarbeitet wird. Dadurch kann der Browser die bereits bereitgestellten Teile der Vorlage anzeigen, was die Benutzeroberfläche erheblich verbessert, da die Benutzeroberfläche nicht hängen bleibt.

Mischer Aufschubcode:

 export default function (count = 10) { return { data () { return { displayPriority: 0 } }, mounted () { this.runDisplayPriority() }, methods: { runDisplayPriority () { const step = () => { requestAnimationFrame(() => { this.displayPriority++ if (this.displayPriority < count) { step() } }) } step() }, defer (priority) { return this.displayPriority >= priority } } } } 

Lazy Component Loading


Lassen Sie uns nun über das Importieren von untergeordneten Komponenten sprechen:

 import Heavy from 'Heavy.js' export default { components: { Heavy } } 

Beim herkömmlichen Import wird die untergeordnete Komponente sofort geladen, sobald der Browser die Importanweisung erreicht. Wenn Sie den Collector verwenden, wird Ihre untergeordnete Komponente in das allgemeine Bundle aufgenommen.

Wenn Ihre untergeordnete Komponente jedoch sehr groß ist, ist es sinnvoll, sie asynchron zu laden.
Es ist sehr einfach zu tun:

 const Heavy = () => import('Heavy.js') export default { components: { Heavy } } 

Das ist alles was Sie brauchen . Vue kann sofort mit Komponenten arbeiten, die faul geladen werden. Er lädt die Komponente bei Bedarf selbst herunter und zeigt sie an, sobald sie fertig ist. Sie können diesen Ansatz zum langsamen Laden überall verwenden.

Wenn Sie den Collector verwenden, wird alles, was mit Heavy.js zu tun hat, in einer separaten Datei gespeichert. Auf diese Weise reduzieren Sie das Gewicht der Dateien während des ersten Downloads und erhöhen die Anzeigegeschwindigkeit.

Lazy Lade Ansichten


Lazy Loading ist sehr nützlich bei Bauteilen, die für Routen verwendet werden. Da Sie nicht alle Komponenten für Routen gleichzeitig herunterladen müssen, empfehle ich immer diesen Ansatz:

 const Home = () => import('Home.js') const About = () => import('About.js') const Contacts = () => import('Contacts.js') new VueRouter({ routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/contacts', component: Contacts }, ] }) 

Jede dieser Komponenten wird nur geladen, wenn der Benutzer die angegebene Route zum ersten Mal anfordert. Und nicht vorher.

Dynamische Komponenten


Ebenso können Sie das verzögerte Laden mit dynamischen Komponenten verwenden:

 <template> <div> <component :is="componentToShow"/> </div> </template> <script> export default { props: ['value'], computed: { componentToShow() { if (this.value) { return () => import('TrueComponent.js') } return () => import('FalseComponent.js') } } } </script> 

Auch hier wird jede Komponente nur geladen, wenn sie benötigt wird.

Arbeite mit Vuex


Vermeiden Sie große Verpflichtungen


Stellen Sie sich vor, Sie müssen eine große Datenmenge im Speicher speichern:

 fetchItems ({ commit }, { items }) { commit('clearItems') commit('addItems', items) //   10000  } 

Das Problem ist das hier. Alle Festschreibungen sind synchrone Vorgänge. Dies bedeutet, dass die Verarbeitung eines großen Arrays Ihre Schnittstelle für die Dauer der Arbeit blockiert.

Um dieses Problem zu lösen, können Sie das Array in Teile aufteilen und einzeln hinzufügen, sodass der Browser Zeit hat, einen neuen Frame zu zeichnen:

 fetchItems ({ commit }, { items, splitCount }) { commit('clearItems') const queue = new JobQueue() splitArray(items, splitCount).forEach( chunk => queue.addJob(done => { //       requestAnimationFrame(() => { commit('addItems', chunk) done() }) }) ) //      await queue.start() } 

Je kleiner die Teilnehmer sind, die Sie dem Speicher hinzufügen, desto reibungsloser bleibt Ihre Benutzeroberfläche und desto länger ist die Gesamtzeit, um die Aufgabe abzuschließen.

Darüber hinaus können Sie mit dieser Methode eine Download-Anzeige für den Benutzer anzeigen. Das wird auch seine Erfahrung mit der Anwendung erheblich verbessern.

Deaktivieren Sie die Reaktivität, wenn sie nicht benötigt wird


Und das letzte Beispiel für heute. Wir haben eine ähnliche Aufgabe: Wir fügen dem Speicher ein Array sehr großer Objekte mit einer Reihe von Verschachtelungsebenen hinzu:

 const data = items.map( item => ({ id: uid++, data: item, // <--    vote: 0 }) ) 

Tatsache ist, dass Vue alle verschachtelten Felder rekursiv durchläuft und sie reaktiv macht.Wenn Sie über viele Daten verfügen, kann dies kostspielig sein, was jedoch viel wichtiger ist - unnötig.

Wenn Ihre Anwendung so aufgebaut ist, dass sie nur vom Objekt der obersten Ebene abhängt und nicht auf reaktive Daten in mehreren Ebenen darunter verweist, können Sie die Reaktivität deaktivieren und so Vue vor unnötiger Arbeit bewahren:

 const data = items.map( item => optimizeItem(item) ) function optimizeItem (item) { const itemData = { id: uid++, vote: 0 } Object.defineProperty(itemData, 'data', { //    "-" configurable: false, value: item }) return itemData } 

Source: https://habr.com/ru/post/de483042/


All Articles