Verwendung von JavaScript-Modulen in der Produktion: aktueller Stand der Dinge. Teil 2

Heute veröffentlichen wir den zweiten Teil der Übersetzung des Materials, der der Verwendung von JS-Modulen in der Produktion gewidmet ist.



→ Hier ist übrigens der erste Teil des Artikels.

Dynamischer Import


Einer der Nachteile der Verwendung realer Importausdrücke zum Trennen von Code- und Lademodulen besteht darin, dass die Aufgabe der Arbeit mit Browsern, die keine Module unterstützen, beim Entwickler liegt.

Und wenn Sie dynamische import() Befehle verwenden möchten, um das Laden von verzögertem Code zu organisieren, müssen Sie sich unter anderem damit auseinandersetzen, dass einige Browser, obwohl sie mit Sicherheit Module unterstützen, dynamische import () - Befehle immer noch nicht unterstützen (Edge 16–18, Firefox 60–66, Safari 11, Chrome 61–63).

Glücklicherweise hilft uns dieses Problem bei der Lösung einer kleinen (ca. 400 Byte großen) und extrem schnellen Polyfüllung .

Das Hinzufügen dieser Polyfüllung zu einem Webprojekt ist sehr einfach. Sie müssen es nur importieren und am Haupteinstiegspunkt der Anwendung initialisieren (bevor Sie einen der import() Befehle im Code aufrufen):

 import dynamicImportPolyfill from 'dynamic-import-polyfill'; //         .   //    ,       . dynamicImportPolyfill.initialize({modulePath: '/modules/'}); 

Das letzte, was getan werden muss, damit dieses Schema funktioniert, ist, Rollup mitzuteilen, dass es die im Code angezeigten dynamischen import() Befehle unter Verwendung des von Ihnen gewählten Namens umbenennen muss (über die Option output.dynamicImportFunction ). Eine Polyfüllung, die die dynamische Importfunktion implementiert, verwendet __import__ Namen __import__ , kann jedoch angepasst werden .

Der Grund, warum Sie import() Ausdrücke umbenennen müssen, liegt darin, dass import in JavaScript ein Schlüsselwort ist. Dies bedeutet, dass es mit Polyfill-Mitteln unmöglich ist, das Ersetzen des Standardbefehls import() durch einen gleichnamigen Befehl zu organisieren. Wenn Sie dies versuchen, tritt ein Syntaxfehler auf.

Es ist jedoch sehr gut, dass Rollup die Befehle während der Erstellung des Projekts umbenennt, da dies bedeutet, dass Sie Standardbefehle im Quellcode verwenden können. Darüber hinaus muss in Zukunft, wenn die Polyfüllung nicht mehr benötigt wird, der Quellcode des Projekts nicht neu geschrieben werden, um zu ändern, was zuvor anders benannt wurde.

Effizientes Laden von JavaScript


Wenn Sie die Codetrennung verwenden, schadet es nicht, das Vorladen aller Module zu organisieren, von denen Sie wissen, dass sie sehr bald geladen werden (dies sind beispielsweise alle Module im Abhängigkeitsbaum des Hauptmoduls, das der Einstiegspunkt für das Projekt ist).

Wenn wir jedoch echte JavaScript-Module laden (über <script type="module"> und dann über die entsprechenden import ), müssen wir das Attribut modulepreload anstelle des üblichen Preloads verwenden, das nur für klassische Skripte vorgesehen ist.

 <link rel="modulepreload" href="/modules/main.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-one.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-two.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-three.XXXX.mjs"> <!-- ... --> <script type="module" src="/modules/main.XXXX.mjs"></script> 

Tatsächlich ist das modulepreload Modulen beim Vorladen realer Module definitiv besser als der herkömmliche Mechanismus zum modulepreload . Tatsache ist, dass bei der Verwendung nicht nur die Datei heruntergeladen wird. Darüber hinaus wird das Skript sofort außerhalb des Hauptthreads analysiert und kompiliert. Ein reguläres preload kann dies nicht tun, da es während des Vorladens nicht weiß, ob die Datei als Modul oder als reguläres Skript verwendet wird.

Dies bedeutet, dass das Laden von Modulen mit dem Attribut modulepreload häufig schneller ist und dass beim Initialisieren von Modulen die Wahrscheinlichkeit geringer ist, dass der Hauptthread übermäßig belastet wird, was zu Schnittstellenproblemen führt.

Erstellen einer Liste von Modulen zum Vorladen


Das Eingabefragment im Rollup- Bundle- Objekt enthält eine vollständige Liste der Importe in ihren statischen Abhängigkeitsbäumen. Daher ist es im Rollup-Hook " generateBundle " einfach, eine Liste der Dateien abzurufen, die vorinstalliert werden müssen.

Obwohl Plugins in npm zum Generieren von Modul-Vorladelisten zu finden sind, erfordert das Erstellen einer ähnlichen Liste für jeden Eingabepunkt im Abhängigkeitsbaum nur wenige Codezeilen. Daher ziehe ich es vor, solche Listen manuell mit folgendem Code zu erstellen:

 {  generateBundle(options, bundle) {    //         .    const modulepreloadMap = {};    for (const [fileName, chunkInfo] of Object.entries(bundle)) {      if (chunkInfo.isEntry || chunkInfo.isDynamicEntry) {        modulepreloadMap[chunkInfo.name] = [fileName, ...chunkInfo.imports];      }    }    //  -   ...    console.log(modulepreloadMap);  } } 

Hier ist zum Beispiel, wie ich eine Modul-Vorladeliste für philipwalton.com und für meine Demo-Anwendung erstellt habe , über die wir weiter unten sprechen werden.

Beachten Sie, dass das Attribut modulepreload definitiv besser ist als das klassische preload zum Laden von modulepreload , jedoch die schlechteste Browserunterstützung bietet (derzeit wird es nur von Chrome unterstützt). Wenn ein erheblicher Teil Ihres Datenverkehrs nicht von Chrome stammt, ist es in Ihrer Situation möglicherweise sinnvoll, anstelle des preload reguläres preload zu preload .

In Bezug auf die Verwendung der preload möchte ich Sie vor etwas warnen. Tatsache ist, dass beim Laden von Skripten mit preload im Gegensatz zu modulepreload diese Skripte nicht in die Browser- modulepreload gelangen. Dies bedeutet, dass die Möglichkeit besteht, dass Preload-Anforderungen mehrmals ausgeführt werden können (z. B. wenn das Modul die Datei importiert, bevor der Browser das Preload abgeschlossen hat).

Warum echte Module in der Produktion einsetzen?


Wenn Sie bereits einen Bundler wie das Webpack verwenden und bereits Code aufteilen und die entsprechenden Dateien vorladen (ähnlich wie ich gerade gesagt habe), fragen Sie sich möglicherweise, ob Sie zu einer Strategie wechseln sollten konzentrierte sich auf die Verwendung von realen Modulen. Es gibt mehrere Gründe, die mich glauben lassen, dass Sie in Betracht ziehen sollten, auf Module umzusteigen. Darüber hinaus glaube ich, dass die Konvertierung eines Projekts in echte Module besser ist als die Verwendung klassischer Skripte mit eigenem Code zum Laden von Modulen.

▍ Reduzieren der Gesamtmenge an Code


Wenn das Projekt echte Module verwendet, müssen Benutzer moderner Browser keinen zusätzlichen Code herunterladen, der zum Laden von Modulen oder zum Verwalten von Abhängigkeiten entwickelt wurde. Wenn Sie beispielsweise echte Module verwenden, müssen Sie die Laufzeitmechanismen und das Webpack-Manifest nicht laden.

▍ Verbesserter Preload-Code


Wie im vorherigen Abschnitt erwähnt, können modulepreload mit dem Attribut modulepreload Code modulepreload und ihn außerhalb des Hauptthreads modulepreload und kompilieren. Alles andere bleibt im Vergleich zum preload Attribut gleich. Dies bedeutet, dass Seiten dank des modulepreload von modulepreload schneller interaktiv werden und die Wahrscheinlichkeit verringert wird, dass der Hauptstrom während der Benutzerinteraktion blockiert wird.

Unabhängig davon, in welcher Größe der Anwendungscode in Fragmente unterteilt ist, ist es daher viel produktiver, diese Fragmente mithilfe der Importbefehle und des Attributs modulepreload , als sie mit dem üblichen script Tag und dem üblichen preload Attribut zu laden (insbesondere, wenn die entsprechenden Tags generiert werden) dynamisch und zur Laufzeit zum DOM hinzugefügt).

Mit anderen Worten, ein Rollup-Bundle eines Projektcodes, der aus 20 Modulfragmenten besteht, wird schneller geladen als ein Bundle desselben Projekts, das aus 20 klassischen Skriptfragmenten besteht, die von webpack erstellt wurden (nicht wegen der Verwendung von webpack, sondern weil dass dies keine echten Module sind).

▍ Verbesserung des zukünftigen Fokus von Code


Viele großartige neue Funktionen von Browsern basieren auf Modulen und nicht auf klassischen Skripten. Wenn Sie diese Funktionen verwenden möchten, sollte Ihr Code in Form von realen Modulen dargestellt werden. Es sollte nicht in ES5 transpiliert und mit den Mitteln des klassischen script Tags geladen sein (dies ist das Problem, über das ich geschrieben habe, als ich versucht habe, die experimentelle KV-Speicher-API zu verwenden ).

Hier sind einige der interessantesten neuen Browserfunktionen, die sich ausschließlich auf Module konzentrieren:


Legacy-Browser-Unterstützung


Weltweit unterstützen mehr als 83% der Browser JavaScript-Module (einschließlich des dynamischen Imports). Daher können die meisten Benutzer ohne besonderen Aufwand seitens der Entwickler dieses Projekts mit einem Projekt arbeiten, das auf Module umgestellt wurde.

Bei Browsern, die Module unterstützen, aber keinen dynamischen Import unterstützen, wird empfohlen, die oben beschriebene Polyfill für den dynamischen Import und die Polyfüllung zu verwenden . Da es sehr klein ist und nach Möglichkeit die standardmäßige browserbasierte import() -Methode verwendet, hat die Verwendung dieser Polyfüllung fast keinen Einfluss auf die Größe oder Leistung des Projekts.

Wenn wir über Browser sprechen, die Module absolut nicht unterstützen, können Sie das Modul / Nomodule-Muster verwenden , um die Arbeit mit ihnen zu organisieren.

▍Bedienungsbeispiel


Da es immer einfacher ist, über die Cross-Browser-Kompatibilität zu sprechen, als sie zu erreichen, habe ich eine Demo-Anwendung erstellt , die die oben beschriebenen Technologien verwendet.

Diese Anwendung funktioniert in Browsern wie Edge 18 und Firefox ESR, die keine dynamischen import() -Befehle unterstützen. Darüber hinaus funktioniert es in Browsern wie Internet Explorer 11, die keine Module unterstützen.

Um zu zeigen, dass die hier diskutierte Strategie nicht nur für einfache Projekte geeignet ist, habe ich in dieser Anwendung viele Funktionen verwendet, die heute in großen Projekten benötigt werden:

  • Codetransformation mit Babel (einschließlich JSX).
  • CommonJS-Abhängigkeiten (z. B. Reagieren und Reagieren).
  • CSS-Abhängigkeiten.
  • Ressourcen-Hashing
  • Codetrennung
  • Dynamischer Import (mit einem Fallback als Polyfill).
  • Implementierung des Modul / Nomodul-Musters.

Der Projektcode befindet sich auf GitHub ( dh Sie können das Repository aufteilen und das Projekt selbst erstellen). Die Demoversion wird auf Glitch gehostet, sodass Sie damit experimentieren können.

Das Wichtigste im Demo-Projekt ist die Rollup-Konfiguration , da sie bestimmt, wie die resultierenden Module erstellt werden.

Zusammenfassung


Ich hoffe, dieses Material hat Sie nicht nur von der Möglichkeit überzeugt, Standard-JavaScript-Module in der Produktion bereitzustellen, sondern auch davon, dass es die Ladezeit der Site und ihre Leistung wirklich verbessern kann.

Hier ist eine kurze Übersicht über die Schritte, die Sie ausführen müssen, um die Module im Projekt zu implementieren:

  • Verwenden Sie einen Bundler unter den unterstützten Ausgabeformaten, für die ES2015-Module vorhanden sind.
  • Aggressive Annäherung an die node_modules (wenn möglich bis zur Zuordnung von Abhängigkeiten von node_modules zu separaten Fragmenten).
  • Laden Sie alle Module in Ihrem statischen Abhängigkeitsbaum vor (mithilfe von modulepreload ).
  • Verwenden Sie polyfill für Browser, die keine dynamischen import() -Anweisungen unterstützen.
  • Verwenden Sie das Modul- / Nomodelmuster, um die Arbeit mit Browsern zu organisieren, die keine Module unterstützen.

Wenn Sie Rollup bereits zum Erstellen des Projekts verwenden, möchten wir, dass Sie versuchen, worüber ich hier gesprochen habe, und echte Module in der Produktion bereitstellen (mithilfe von Codetrennung und dynamischen Importtechniken). Wenn Sie dies tun, lassen Sie mich wissen, wie es Ihnen geht. Ich interessiere mich für Probleme und erfolgreiche Fälle der Einführung von Modulen.

Es ist sehr klar, dass Module die Zukunft von JavaScript sind. Ich würde gerne und vorzugsweise bald sehen, wie die von uns verwendeten Tools und die Hilfsbibliotheken diese Technologie übernehmen. Ich hoffe, dass dieses Material diesen Prozess zumindest ein wenig unterstützen kann.

Liebe Leser! Verwenden Sie JS-Module in der Produktion?

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


All Articles