JavaScript-Bündelung und Leistung: Best Practices

Jetzt, zur Wende des Jahrzehnts, ist es an der Zeit, das, was in der jüngeren Vergangenheit als richtig galt, kritisch zu überschätzen und herauszufinden, ob es in unseren Tagen an Relevanz verloren hat. Aus den Best Practices von gestern werden manchmal die Antimuster von heute.



Der Autor des Artikels, dessen Übersetzung wir heute veröffentlichen, wird drei Ansätze zur Bündelung von JavaScript-Projekten am Beispiel einer einfachen Hello World-Anwendung untersuchen, die mit React erstellt wurde. Zu den Beispielen, die er anführt, gehört das Lesen der Grundlagen von Modul-Buildern, wie z. B. Webpack , das heute das beliebteste Tool zu sein scheint.

Ansatz Nr. 1: Absolut alles kommt ins Bündel (es sieht aus wie ein großer Fadenballen)


Die Hauptidee: Verwenden Sie diesen Ansatz nicht.

Bei diesem Ansatz wird der Modul-Builder einfach verwendet, um alles im Bundle zu packen - sowohl Abhängigkeiten als auch Anwendungscode. Die Ausgabe ist so etwas wie ein großer Wollknäuel. In meinem Beispiel sind dies die Angaben " react , " react-dom und der Code der Anwendung selbst. Ein einzelnes Bundle mit dem gesamten Projektcode ist mit der Seite verbunden:

 <!-- index.html --> <script src="bundle.[hash].min.js"></script> 

Vor der Veröffentlichung von HTTP / 2 konnte dieses Muster in gewisser Weise als akzeptabel angesehen werden, da durch seine Verwendung die Anzahl der HTTP-Anforderungen verringert wird, die der Browser beim Laden von Seitenmaterialien ausführt. Angesichts der Tatsache, dass die meisten Sites heutzutage HTTP / 2 verwenden, ist dieses Muster zu einem Antimuster geworden.

Warum? Tatsache ist, dass bei Verwendung von HTTP / 2 die Ausführung vieler Anforderungen nicht mehr die gleiche Systemlast wie zuvor erzeugt. Infolgedessen bietet das Packen des Codes in ein einzelnes großes Bündel dem Projekt keine signifikanten Vorteile mehr.

Mit diesem Ansatz ist die effiziente Organisation des Browser-Cachings kompliziert. Wenn Sie beispielsweise in einer einfachen Anwendung nur eine Codezeile ändern, wird der Bundle-Hash geändert und der Cache ungültig, in dem der gesamte Projektcode gespeichert ist. Infolgedessen müssen alle wiederkehrenden Besucher den gesamten Site-Code erneut herunterladen, obwohl dieser Code zu 99% nicht von dem Code abweicht, den sie beim vorherigen Besuch heruntergeladen haben. Hier geht es um den irrationalen Einsatz von Netzwerkressourcen aufgrund der wiederholten Übertragung der gleichen Daten vom Server zum Client.

HTTP / 2 wird heute von mehr als 95% der Clients unterstützt . Im Jahr 2019 wurde dieses Protokoll von den meisten Servern implementiert. Weitere Informationen zur Verwendung von HTTP / 2 im Jahr 2019 finden Sie hier.

Ansatz Nr. 2: Separate Verpackung des Projektcodes und des Codes von Fremdbibliotheken (Codetrennung)


Hauptidee: Bitte verwenden Sie diesen Ansatz.

Lassen Sie uns berücksichtigen, worüber wir im vorherigen Abschnitt gesprochen haben, und die Situation beim Browser-Caching verbessern, indem Sie den Projektcode vom Abhängigkeitscode trennen. Dies löst das Problem in dem oben beschriebenen Fall, wenn wir den Projektcode geringfügig ändern und anschließend das Update in der Produktion veröffentlichen. Jetzt hat sich nur der Hash des index Bundles geändert, in dem der projekteigene Code gespeichert ist, und der Hash des vendor Bundles bleibt unverändert. Besucher, die zur aktualisierten Site zurückkehren, laden nur die geänderte index mit dieser Methode herunter, wodurch einige Netzwerkressourcen gespart werden.

Wenn wir über Webpack sprechen, benötigen Sie zur Implementierung dieser Strategie zusätzliche Einstellungen für die Codetrennung. Mit ihrer Hilfe informieren wir den Bundler darüber, wo sich der Abhängigkeitscode befindet. Ein einfacher und bequemer Weg, solchen Code zu erkennen, besteht darin, dass sich im Pfad zu den entsprechenden Dateien node_modules befinden, da der gesamte Abhängigkeitscode in diesem Ordner gespeichert ist.

Folgendes ist der Verbindungscode des Skripts:

 <!-- index.html --> <script src="vendor.[hash].min.js"></script> <script src="index.[hash].min.js"></script> 

Und hier ist die Wasserfall-Timeline zum Laden der Seite, wenn mit HTTP / 2 gearbeitet wird.


Ladezeitplan für Wasserfall-Seiten

Dies ist ein Schritt in die richtige Richtung. Wir können die Bündelung aber weiter optimieren. Wenn Sie darüber nachdenken, können Sie verstehen, dass sich einige Projektabhängigkeiten seltener ändern als andere. Vielleicht ändern sich react und react-dom wenigsten react und wenn eine Bibliothek aktualisiert wird, wird eine andere aktualisiert. Als Ergebnis können wir schließen, dass diese beiden Bibliotheken zu einem logischen Fragment zusammengefasst werden können, das von anderen Abhängigkeiten getrennt werden kann, die sich häufiger ändern als react und react-dom . Wenn wir diese Idee verwirklichen, erhalten wir Folgendes:

 <!-- index.html --> <script src="vendor.react.[hash].min.js"></script> <script src="vendor.others.[hash].min.js"></script> <script src="index.[hash].min.js"></script> 

Wenn wir diese Idee weiterentwickeln, müssen Website-Besucher möglicherweise nicht den gesamten Projektcode herunterladen, um nur eine Seite anzuzeigen. Im Winter nehmen einige von uns zu - und so wächst die index , die den Anwendungscode enthält, mit der Zeit. Irgendwann kann sich herausstellen, dass der Projektcode zu Recht einer weiteren Trennung unterzogen wird, indem einzelne Komponenten dynamisch geladen und sogar das Vorladen einzelner Module organisiert wird.

Für mich scheint diese Ebene der Codetrennung immer noch ziemlich entmutigend. Es sieht immer noch nach experimenteller Technologie aus (und so ähnlich, in der sich subtile Fehler manifestieren können). Mir ist jedoch sehr klar, dass eine starke Codetrennung die Richtung ist, in die sich die Webentwicklungsbranche bewegt. Vielleicht werden wir aufgrund der Browserunterstützung für JavaScript-Module in der Lage sein, Bundler wie Webpack vollständig aufzugeben und den Clients einfach einzelne Codemodule zuzuweisen. Es wird interessant sein zu sehen, wohin uns das alles führt!

Ansatz 3: Verwenden öffentlicher CDNs für den Code einiger Abhängigkeiten


Die Hauptidee: Verwenden Sie diesen Ansatz nicht.

Wenn Sie einer von denen sind, die in der Webentwicklung ein bisschen altmodisch sind (wie ich), dann haben Sie möglicherweise das interne Gefühl, dass wir möglicherweise die Datei vendor.react verbinden können, die im Abschnitt „Vorgehensweise Nr. 2 "mit einer öffentlichen CDN-Ressource:

 <!-- index.html --> <script crossorigin src="https://unpkg.com/react@16.12.0/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.production.min.js"></script> <script src="index.[hash].min.js"></script> 

Ich stelle fest, dass es bei Verwendung dieses Ansatzes erforderlich ist , dem Webpack-Projektkollektor mitzuteilen, dass er den Reaction- und Reaction react-dom Code aus dem Bundle ausschließen soll.

Auf den ersten Blick sieht das alles ganz gut aus, aber dieser Ansatz hat einige Nachteile, die ich in Betracht ziehen möchte.

▍ Minus 1: Verwenden verschiedener Standorte derselben Abhängigkeitsdateien? Nicht mehr…


Die Entwickler der alten Schule hoffen, dass, wenn alle Websites auf dieselbe CDN-Ressource verweisen und dieselbe Version von React wie unsere Website verwenden, die Besucher unserer Website, wenn sich bereits ein React-Code im Cache ihres Browsers befindet, Verschwenden Sie keine Zeit, um es neu zu laden. Dies würde die Geschwindigkeit der Anzeige von Seiten unserer Website im Arbeitsmodus erheblich erhöhen. Ja, und die React- Dokumentation zu diesem Thema sieht vielversprechend aus. Daher verwenden einige Entwickler mit Sicherheit dieses Muster. Richtig?

Obwohl dies in letzter Zeit in Browsern zur Erhöhung der Sicherheit durchaus funktionieren könnte, haben sie damit begonnen, einen Mechanismus für die gemeinsame Nutzung von Caches zu implementieren. Wir sprechen über die Tatsache, dass selbst unter idealen Bedingungen, wenn zwei Sites dieselbe Bibliothek verwenden, die über denselben CDN-Link geladen wird, der Code für jede Domain unabhängig heruntergeladen wird und der Cache aus Gründen des Datenschutzes in der Sandbox landet einer bestimmten Domain zugeordnet. Wie sich herausstellte , wurde dieser Mechanismus bereits in Safari implementiert (anscheinend existiert er seit 2013?!). Wenn wir über Chrome 77 sprechen, müssen Sie ein spezielles Flag verwenden , um die Cache-Trennung für den Moment zu aktivieren.

Es wird davon ausgegangen, dass die Verwendung öffentlich verfügbarer CDNs abnimmt, wenn die Cache-Freigabe in mehr Browsern implementiert wird.

▍ Minus 2: Verschwendung von Systemressourcen bei Hilfsoperationen (für jede Domäne)


Hier wird die Idee geäußert, dass die Verwendung von CDN zu einer Erhöhung der Systemlast führt, da der Browser bereits vor dem Senden der HTTP-Anfrage viele Probleme lösen muss: DNS-Namensauflösung, TCP-Verbindung, SSL-Handshake. Um eine Verbindung mit der Site herzustellen, muss der Browser diese Aktionen in jedem Fall ausführen. Wenn jedoch auch eine Verbindung mit dem CDN hergestellt werden muss, erhöht dies die Belastung für die Site.

Hier ist ein Wasserfalldiagramm, das den Vorgang des Ladens einer Seite veranschaulicht, die Skripte aus einer öffentlich verfügbaren CDN-Ressource verwendet.


Wasserfall-Timeline zum Laden einer Seite unter Verwendung öffentlich verfügbarer CDN-Ressourcen

Die roten Ovale markieren die Bereiche, in denen Vorgänge ausgeführt werden, die der Ausführung von Abfragen vorausgehen. Dies scheint ein bisschen zu viel für eine einfache Hello World-Anwendung.

Da sich mein einfaches Beispiel weiterentwickelt und erweitert, wird es eine Zeit geben, in der ich meine eigene Schriftart verwenden möchte. Zum Beispiel - entnommen aus Google Fonts. Dies bedeutet, dass die Anzahl solcher Verzögerungen nur zunimmt, da Sie eine Verbindung zur entsprechenden Domäne herstellen müssen, um Schriftarten herunterzuladen. Hier scheint die Idee, alle Ressourcen der Site in einer eigenen Hauptdomäne zu hosten (die sich natürlich hinter der projekteigenen CDN-Ressource auf Basis von Cloudflare oder Cloudfront befindet), sehr attraktiv zu sein.

Wenn wir in unserem Beispiel zwei React-Abhängigkeiten von der Hauptdomäne der Site herunterladen, führt dies dazu, dass der Wasserfall-Zeitplan für das Laden von Seiten viel genauer wird.


Ladezeitplan für Wasserfälle für eine Seite, die keine öffentlich verfügbaren CDN-Ressourcen verwendet

▍Minus Nr. 3: Verwenden verschiedener Versionen von Abhängigkeiten von verschiedenen Standorten


Mit React habe ich eine kleine Studie zu den 32 größten Standorten erstellt. Leider habe ich herausgefunden, dass nur 10% von ihnen öffentlich verfügbare CDN-Ressourcen zum Herunterladen von React verwenden. Angesichts der Versionen von React, die von allen untersuchten Sites verwendet werden, stellte sich jedoch heraus, dass dies nicht wirklich wichtig ist. In einer idealen Welt würde es keine Trennung des Browser-Cache geben, und alle Sites könnten organisiert werden und dieselben Skriptversionen aus denselben öffentlichen CDNs verwenden. In der Realität gibt es extrem starke Unterschiede in den Versionen von React, die von verschiedenen Sites verwendet werden. Dies zerstört die Idee eines gemeinsam genutzten Browser-Cache.


Von verschiedenen Sites verwendete Versionen reagieren

Wenn Sie zuerst eine beliebte Site mit React öffnen und dann eine andere, ist die Wahrscheinlichkeit sehr gering, dass beide Sites dieselbe Version von React verwenden.

Im Laufe der Recherche habe ich weitere interessante Informationen zu diesen React-Sites gefunden. Vielleicht erscheinen sie Ihnen auch interessant:

  • Auf 2/3 Sites wird Webpack zum Erstellen von Code verwendet.
  • 87% der Websites verwenden HTTP / 2. Dies ist mehr als der Durchschnittswert von 58%.
  • Die meisten Projekte (ca. 56%) hosten die Schriftarten selbst.

Hier sind die Quelldaten für mein Experiment.

▍Minus Nr. 4: Probleme mit Nichterfüllung


Leider haben diejenigen, die öffentlich verfügbare CDN-Ressourcen nutzen, heute nicht nur Probleme mit der Geschwindigkeit beim Laden von Seiten, sondern auch mit einigen anderen Problemen:

  • Sicherheitsfragen. Mit der Verwendung öffentlich verfügbarer CDN-Ressourcen sind andere Sicherheitsprobleme verbunden als die, mit denen die Cache-Freigabe in Browsern behoben werden soll. Wenn beispielsweise Hacker in eine öffentlich zugängliche CDN-Ressource eindringen, können sie schädlichen JavaScript-Code sehr sorgfältig in Bibliotheken einfügen. Ein solcher Code verfügt über alle Berechtigungen des Site-Codes, der auf dem Client ausgeführt wird. Er kann mit den Daten von Benutzern arbeiten, die sich auf der Site angemeldet haben.
  • Datenschutzprobleme. Viele Unternehmen erheben Benutzerdaten aus Anfragen von Drittanbietern. Wenn die Verwendung einer öffentlich verfügbaren CDN-Ressource zum Herunterladen von Abhängigkeitscode oder Zeichensätzen auf allen Sites implementiert ist, kann diese CDN-Ressource theoretisch Benutzersitzungen und die Funktionen ihrer Arbeit im Internet verfolgen. Eine ähnliche Ressource kann Annahmen darüber treffen, was sie interessiert (zum Beispiel für Werbezwecke). Und das alles - ohne den Einsatz von Cookies!
  • Single Point of Failure. Durch die Verteilung der für das Funktionieren der Website erforderlichen Materialien auf mehrere Domänen steigt die Wahrscheinlichkeit, dass der Client aufgrund von Problemen in einer dieser Domänen nur einen Teil der Materialien herunterladen kann, die erforderlich sind, um die Seiten funktionsfähig zu machen. Um ehrlich zu sein, können solche Probleme mit JavaScript gelöst werden, aber der Website-Entwickler muss hierfür einige zusätzliche Anstrengungen unternehmen.

Zusammenfassung


Es ist sehr offensichtlich, dass die Zukunft bei Ansatz Nr. 2 liegt.

Hosten Sie Ihre eigenen CDN-Ressourcen vor Ihren Servern mithilfe von HTTP / 2 (z. B. Cloudflare oder Cloudfront). Teilen Sie den Code in kleine Fragmente auf, um den Browser-Cache effektiv zu nutzen. In Zukunft werden die Fragmente, in die der Site-Code unterteilt ist, möglicherweise noch kleiner und erreichen die Größe einzelner JavaScript-Module , da die Browser die Unterstützung für diese Technologie bereits implementiert haben.

Sehr geehrte Leser! Verwenden Sie Codetrennungstechnologien in Ihren Webprojekten?

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


All Articles