Reduzieren der Bundle-Größe mit Webpack Analyzer und React Lazy / Suspense

Mit zunehmender Komplexität von Clientanwendungen wird die Größe ihrer Bundles immer größer. In dieser Situation leiden die Menschen am meisten und sind aus verschiedenen Gründen gezwungen, langsame Internetverbindungen zu nutzen. Außerdem wird es jeden Tag schlimmer.



Der Autor des Artikels, dessen Übersetzung wir heute veröffentlichen, arbeitet in Wix. Er möchte darüber sprechen, wie er mit Webpack Analyzer und React Lazy / Suspense die Größe eines Bundles um etwa 80% reduzieren konnte.

Wie früh soll die Optimierung beginnen?


Wenn Sie gerade mit der Arbeit an Ihrer neuen Webanwendung begonnen haben, versuchen Sie wahrscheinlich, sich darauf zu konzentrieren, sie sozusagen „auf den Weg zu bringen“ und sie zum Laufen zu bringen. Sie achten wahrscheinlich nicht besonders auf die Leistung oder die Bundle-Größe. Ich kann das verstehen Meine Erfahrung zeigt jedoch, dass die Leistung und die Größe der Bundles von Anfang an berücksichtigt werden sollten. Eine gute Anwendungsarchitektur und eine zeitnahe „Reflexion über die Zukunft des Projekts“ sparen Ihnen auf lange Sicht viel Zeit und tragen dazu bei, keine ernsthaften technischen Schulden zu akkumulieren. Natürlich ist es schwierig, alles im Voraus vorauszusehen, aber Sie sollten sich vom ersten Arbeitstag an sehr bemühen, alles richtig zu machen.

Hier sind einige großartige Tools, die meiner Meinung nach von Anfang an verwendet werden sollten. Mit diesen Tools können Sie „problematische“ NPM-Pakete erkennen, noch bevor sie einen wichtigen Platz in der Anwendung einnehmen.

UndBundlephobie


Bundlephobia ist eine Website, auf der Sie wissen, um wie viel ein bestimmtes NPM-Paket die Größe eines Bundles erhöht. Dies ist ein großartiges Tool, mit dem der Programmierer die richtigen Entscheidungen hinsichtlich der Auswahl der Pakete von Drittanbietern treffen kann, die er möglicherweise benötigt. Bundlephobia hilft dabei, die Anwendungsarchitektur so zu gestalten, dass ihre Größe nicht zu groß wird. Die folgende Abbildung zeigt die Ergebnisse der Überprüfung einer beliebten Bibliothek auf Zeitarbeit, die als Moment bezeichnet wird. Sie können sehen, dass diese Bibliothek ziemlich groß ist - fast 66 KB in komprimierter Form. Für viele Benutzer, die im Hochgeschwindigkeitsinternet arbeiten, ist dies nichts. Es lohnt sich jedoch, auf die Downloadzeit dieses Pakets in 2G / 3G-Netzwerken zu achten. Sie beträgt 2,2 bzw. 1,32 Sekunden. Und seien Sie vorsichtig, wir sprechen nur über dieses eine Paket.


Ergebnisse der Bundlephobia-Moment-Paketanalyse

▍Kosten importieren


Import Cost ist eine sehr interessante Erweiterung für viele beliebte Code-Editoren (es gibt über eine Million Downloads für VS Code ). Es kann die "Kosten" importierter Pakete anzeigen. Ich mag es besonders, weil es hilft, Problembereiche der Anwendung direkt während der Arbeit zu identifizieren. Die folgende Abbildung (entnommen aus der Seite GitHub importieren) zeigt ein hervorragendes Beispiel für die Auswirkungen eines anderen Ansatzes zum Importieren von Entitäten auf die Projektdimensionen. Der Import der einzigen uniqueId Eigenschaft aus Lodash führt also zum Import der gesamten Bibliothek in das Projekt (70 KB). Wenn Sie die Funktion uniqueId direkt importieren, werden der uniqueId nur 2 uniqueId hinzugefügt. Lesen Sie hier mehr über Importkosten.


Die "Kosten" für den Import der gesamten Lodash-Bibliothek in das Projekt und nur eine bestimmte Funktion aus dieser Bibliothek

Der Fall von unangemessen großen Bündeln


Sie haben also Ihre wunderbare Anwendung erstellt. Es funktioniert hervorragend in Ihrem Highspeed-Internet und auf Ihrem leistungsstärksten Computer mit RAM. Du hast es in die reale Welt entlassen. Nach einiger Zeit kamen Beschwerden von Ihnen oder Ihren eigenen Analysten. Diese Beschwerden betrafen die Ladezeiten der Anwendung. Ähnliches passierte mir kürzlich, als wir bei Wix eine neue Funktion vorstellten, an der ich arbeitete.

Lassen Sie uns über diese neue Gelegenheit sprechen, um Sie ein wenig auf den neuesten Stand zu bringen. Dies ist eine neue Fortschrittsanzeige, die sich oben im Seitenbereich der Benutzeroberfläche für die Einstellungen der Benutzersite befindet. Der Zweck dieses Mechanismus besteht darin, den Benutzer auf die verschiedenen Schritte aufmerksam zu machen, die er ausführen muss, damit sein Geschäftsprojekt bessere Erfolgschancen hat (Verbindung zur Suchmaschinenoptimierung, Hinzufügen von Regionen, in die Waren geliefert werden, Hinzufügen des ersten Produkts) , usw).

Der Fortschrittsbalken wird automatisch aktualisiert, indem über Web-Sockets eine Verbindung zum Server hergestellt wird. Wenn der Benutzer alle empfohlenen Schritte ausgeführt hat, wird ein Popup-Fenster mit Glückwünschen angezeigt. Nachdem dieses Fenster geschlossen wurde, wird der Fortschrittsbalken ausgeblendet und nie wieder angezeigt, wenn Sie mit der Site arbeiten, auf der Sie ihn verwendet haben. Darüber haben wir gerade gesprochen.


Fortschrittsbalken und Glückwunschfenster

Was ist passiert? Warum haben sich unsere Analysten bei mir beschwert, dass sich die Ladezeit der Seite erhöht hat? Als ich den Stand der Dinge auf der Registerkarte "Netzwerk" der Chrome-Entwicklertools untersuchte, wurde mir sofort klar, dass mein Bundle ziemlich groß war. Seine Größe betrug nämlich 190 Kb.


Mit den Chrome Developer Tools gefundene Bundle-Größe

"Warum braucht dieses kleine Ding ein relativ großes Bündel?", Dachte ich dann. Aber die Wahrheit - warum?

▍Suchen Sie nach Problemstellen im Bundle


Nachdem ich festgestellt habe, dass das Bundle zu groß ist, ist es Zeit, den Grund dafür herauszufinden. Hier hat sich Webpack Bundle Analyzer als nützlich erwiesen - ein großartiges Tool zum Identifizieren von Problembereichen von Bundles. Es öffnet sich eine neue Browser-Registerkarte und zeigt Abhängigkeitsinformationen an.

Dies geschah, nachdem ich das Bundle mit diesem Tool analysiert hatte.


Ergebnisse des Webpack Bundle Analyzer

Mit Hilfe des Analysators konnte ich den "Verbrecher" erkennen. Hier wurde das Lottie-Web-Paket verwendet, das die Größe des Bundles um 61,45 KB erhöhte. Lottie ist eine sehr schöne JavaScript-Bibliothek, mit der Sie mithilfe von Standardbrowser-Tools in Adobe After Effect erstellte Animationen ausgeben können. In meinem Fall war es so, dass unser Designer eine schöne Animation brauchte, die ausgeführt wurde, als das Fenster mit Glückwünschen erschien. Er erstellte diese Animation und gab sie mir in Form einer JSON-Datei, die ich in das Lottie-Paket übertrug und eine wunderschöne Animation erhielt. Zusätzlich zum Lottie-Web-Paket hatte ich auch eine JSON-Datei mit Animationsbeschreibungen. Die Größe dieser Datei betrug 26 KB. Infolgedessen „kosten“ mich die Lottie-Bibliothek, die JSON-Datei und einige zusätzliche kleine Abhängigkeiten ungefähr 94 KB. Und dies ist nur eine Animation des Fensters mit Glückwünschen an den Benutzer. Der Benutzer, als er diese Glückwünsche sah, hätte glücklich sein sollen. Das alles machte mich traurig.

React Lazy / Suspense-Technologie hilft


Nachdem ich die Ursache des Problems entdeckt habe, ist es Zeit, dieses Problem zu lösen. Es war klar, dass es nicht notwendig war, zu Beginn der Arbeit alles zu laden, was für die Animation erforderlich war. Tatsächlich bestand eine beträchtliche Chance, dass der Benutzer während der aktuellen Sitzung kein Fenster mit Glückwünschen zeigen musste. Dann lernte ich die kürzlich erschienenen React Lazy / Suspense-Technologien kennen und dachte, dass ich jetzt wahrscheinlich eine gute Gelegenheit hatte, sie zu testen.

Wenn Sie mit dem Konzept der "faulen" (faulen) Komponenten nicht vertraut sind, wissen Sie, dass ihre Bedeutung darin besteht, die Anwendung in kleine Codeteile zu unterteilen. Das Herunterladen dieser Fragmente wird nur durchgeführt, wenn sie benötigt werden. In meinem Fall drückte sich dies darin aus, dass ich aus der Hauptfunktion des Fortschrittsbalkens die Komponente auswählen musste, die für die Anzeige der Glückwünsche verantwortlich war. Diese Komponente musste erst geladen werden, wenn der Benutzer die empfohlene Abfolge von Schritten ausgeführt hatte.

React 16.6.0 (und neuere Versionen) verfügt über eine einfache API, mit der faule Komponenten gerendert werden können. Dies sind React.lazy und React.Suspense . Betrachten Sie ein Beispiel:

 const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() {  return (    <div>      <React.Suspense fallback={<div>Loading...</div>}>        <OtherComponent />      </React.Suspense>    </div>  ); } 

Wir haben eine Komponente, die das <div> -Element anzeigt, und darin befindet sich eine Suspense Komponente, die die OtherComponent Komponente OtherComponent . Wenn Sie sich die erste Zeile dieses Beispiels ansehen, sehen Sie, dass OtherComponent nicht direkt in den Code importiert wird. In der Regel sieht ein import OtherComponent from './OtherComponent'; Import wie der import OtherComponent from './OtherComponent'; .

Stattdessen wird der Importbefehl als eine Funktion umrahmt, die einen Dateipfad verwendet. Dieser Mechanismus funktioniert, da Webpack über integrierte Tools zur Codetrennung verfügt. Wenn der Code eine ähnliche Konstruktion enthält, wird ein Versprechen zurückgegeben, das nach dem Herunterladen der Datei mit dem Inhalt dieser Datei aufgelöst wird. Unser React.lazy ist in eine React.lazy Funktion React.lazy .

In den von MyComponent Rendering-Materialien wird MyComponent in eine React.Suspense Komponente mit einer fallback Eigenschaft eingeschlossen. In unserem Fall stellt sich heraus, dass beim Laden des Renderns OtherComponent Laden der entsprechenden Komponente beginnt. In der Zwischenzeit wird gerendert, was in die fallback Eigenschaft geschrieben wird. In diesem Beispiel ist dies der Text Loading… Das ist in der Tat alles. Diese Mechanismen machen einfach ihren Job.

Es stimmt, es gibt einige Funktionen, die Sie bei der Arbeit mit Lazy / Suspense berücksichtigen müssen.

  1. Eine Komponente, die "faul" importiert wird, muss den Standardexport enthalten, der der Einstiegspunkt der Komponente ist. Benannte Exporte können hier nicht verwendet werden.
  2. Sie müssen die mit der Funktion React.Suspense importierte Komponente in die Komponente React.Suspense . Die React.Suspense Komponente React.Suspense die fallback Eigenschaft bereitstellen. Andernfalls tritt ein Fehler auf. Wenn Sie jedoch erst dann etwas rendern möchten, wenn die Komponente verzögert geladen wurde, können Sie im fallback einfach null schreiben, ohne zu versuchen, die Notwendigkeit zu umgehen, etwas in diese Eigenschaft auf knifflige Weise zu schreiben.

Hat mir die Verwendung von React Lazy / Suspense geholfen?


Ja, es hat geholfen! Die Code-Aufteilung hat einfach unglaublich funktioniert. Werfen wir einen Blick auf die Ergebnisse der Analyse des neuen Bundles mit Webpack.


Ergebnisse des Webpack Bundle Analyzer nach der Codeaufteilung

Wie Sie sehen können, hat sich die Größe meines Bundles um etwa 50% auf 96 KB verringert. Großartig!

Also was, jetzt ist das Problem gelöst? Nein, leider. Als ich mir die Seite ansah, stellte sich heraus, dass das Popup mit Glückwünschen die Position verloren hatte.


Das Glückwunschfenster wird nicht dort angezeigt, wo Sie es benötigen

Das Problem war, dass ich das Fenster "geöffnet" habe, indem ich den Status der React-Komponente geändert habe. In der Zwischenzeit habe ich mit der React.Suspense Komponente bereits null gerendert ( React.Suspense ich habe nichts gerendert). Nach dem verzögerten Laden der erforderlichen Daten wurden die relevanten Materialien zum DOM hinzugefügt. Die Positionierung des Popups wurde jedoch bereits vorgenommen. Aufgrund der Tatsache, dass sich die Eigenschaften der entsprechenden Komponente nicht geändert haben, „wusste“ diese Komponente daher nicht, dass sie das Problem bezüglich der Positionierung lösen musste. Wenn ich die Größe des Browserfensters geändert habe, wurde das Popup-Fenster an der richtigen Stelle angezeigt, da die entsprechende Komponente Änderungen der Eigenschaften und Ereignisse beim Ändern der Fenstergröße beobachtete und gegebenenfalls eine Neupositionierung einleitete.

Wie kann man dieses Problem lösen? Die Lösung bestand darin, den "Vermittler" zu eliminieren.

Ich musste zuerst die "faule" Komponente laden und erst dann in den Zustand schreiben, der dem Fenster mit Glückwünschen mitteilen würde, dass es geöffnet werden muss. Ich konnte dies mit denselben Webpack-Code-Sharing-Mechanismen tun, aber jetzt - ohne Importe mit React.lazy zu implementieren:

 async loadAndSetHappyMoment() {  const component = await import(    '../SidebarHappyMoment/SidebarHappyMoment.component'  );  this.SidebarHappyMoment = component.SidebarHappyMoment;  this.setState({    tooltipLevel: TooltipLevel.happyMoment,  }); } 

Diese Funktion wird aufgerufen, nachdem meine Komponente über den Mechanismus der Web-Sockets darüber informiert wurde, dass ein Fenster mit Glückwünschen angezeigt werden muss. Ich habe die Webpack- import (zweite Codezeile) verwendet. Wenn Sie sich erinnern, habe ich oben gesagt, dass diese Funktion ein Versprechen zurückgibt, sodass ich das Konstrukt async/await verwenden konnte.

Nachdem die Komponente this.SidebarHappyMoment = component.SidebarHappyMoment; , schreibe ich sie in die Instanz meiner Komponente (mit dem Befehl this.SidebarHappyMoment = component.SidebarHappyMoment; ). Dies gibt mir die Möglichkeit, es später beim Rendern zu verwenden. Bitte beachten Sie, dass ich jetzt benannte Exporte verwenden kann. In meinem Fall habe ich in der obigen Zeile den Namen SidebarHappyMoment . Und schließlich, und das ist nicht weniger wichtig als alles andere, „informiere“ ich das Fenster, dass es geöffnet werden muss, und ändere seinen Status entsprechend, nachdem ich weiß, dass die Komponente betriebsbereit ist.

Infolgedessen sieht der Rendering-Code jetzt folgendermaßen aus:

 renderTooltip() {  if (this.state.tooltipLevel === TooltipLevel.happyMoment) {    return <this.SidebarHappyMoment />;  }  // ... } 

Beachten Sie, dass der Befehl return <this.SidebarHappyMoment />; gibt this.SidebarHappyMoment - was ich zuvor in die Instanz meiner Komponente geschrieben habe. Dies ist eine normale synchrone Renderfunktion, genau wie die, die Sie millionenfach verwendet haben. Und jetzt wird das Fenster mit Glückwünschen genau dort angezeigt, wo es angezeigt werden soll. Und die Sache ist, dass es erst geöffnet wird, nachdem sein Inhalt einsatzbereit ist.

Produkt definiert Architektur


Wenn die Idee in der Überschrift dieses Abschnitts in Ihrer Vorstellung ein heftiges Fragezeichen ausgelöst hat, sollten Sie sich bewusst sein, dass dies der Fall ist. Das Produkt definiert die Architektur.

Es geht um die Tatsache, dass das Produkt definiert, was sichtbar und interaktiv sein soll, wenn die Komponente zum ersten Mal gerendert wird. Auf diese Weise kann der Entwickler herausfinden, was genau er vom Hauptcode trennen und bei Bedarf später laden kann. Ich habe über die Situation nachgedacht und mich daran erinnert, dass der Benutzer, nachdem er die empfohlenen Schritte zum Einrichten der Site ausgeführt hat oder nicht der Site-Administrator ist, ihm nicht den Fortschrittsbalken und die damit verbundenen Funktionen anzeigen muss verbunden. Mit diesen Informationen konnte ich das Bundle nun weiter teilen. Das habe ich bekommen.


Die fortgesetzte Trennung des Bündels

Danach betrug die Größe des Bündels nur noch 38 KB. Und ich erinnere Sie daran, dass wir mit 190 Kb angefangen haben. Die Größe des Bündels wird um 80% reduziert. Übrigens sehe ich bereits andere Möglichkeiten zur Codetrennung. Ich kann es kaum erwarten, das Bundle weiter zu optimieren.

Zusammenfassung


Entwickler haben die Möglichkeit, sich zu bemühen, in ihrer „Komfortzone“ zu bleiben und sich nur mit dem Gerät des Codes selbst und seiner Funktionalität zu befassen. Ein Programmierer, der die oben beschriebenen Tools verwendet, kreativ denkt und eng mit anderen Spezialisten zusammenarbeitet, kann möglicherweise die Leistung seiner Anwendung verbessern, indem er die Größe des Bundles, das die für die Arbeit mit dieser Anwendung erforderlichen Informationen enthält, erheblich reduziert.

Liebe Leser! Verwenden Sie die Code-Aufteilung, um das Laden Ihrer Webanwendungen zu beschleunigen?


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


All Articles