Module vorab laden

Sérgio Gomes

Veröffentlicht: 23. November 2024

Die modulebasierte Entwicklung bietet einige echte Vorteile in Bezug auf die Cachbarkeit. So können Sie die Anzahl der Bytes reduzieren, die Sie an Ihre Nutzer senden müssen. Die feinere Granularität des Codes hilft auch bei der Ladezeit, da Sie den kritischen Code in Ihrer Anwendung priorisieren können.

Modulabhängigkeiten führen jedoch zu einem Ladeproblem, da der Browser warten muss, bis ein Modul geladen ist, bevor er seine Abhängigkeiten ermitteln kann. Eine Möglichkeit, dies zu umgehen, besteht darin, die Abhängigkeiten vorab zu laden, damit der Browser alle Dateien im Voraus kennt und die Verbindung belegt bleiben kann.

Mit <link rel="preload"> können Ressourcen deklarativ im Voraus angefordert werden, bevor der Browser sie benötigt.

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

Das funktioniert besonders gut mit Ressourcen wie Schriftarten, die oft in CSS-Dateien versteckt sind, manchmal mehrere Ebenen tief. In diesem Fall müsste der Browser mehrere ‑Wege abwarten, bevor er erfährt, dass er eine große Schriftdatei abrufen muss. Er könnte diese Zeit jedoch nutzen, um den Download zu starten und die volle Verbindungsbandbreite zu nutzen.

<link rel="preload"> und das entsprechende HTTP-Header-Äquivalent bieten eine einfache, deklarative Möglichkeit, dem Browser sofort wichtige Dateien mitzuteilen, die im Rahmen der aktuellen Navigation benötigt werden. Wenn der Browser das Preloading erkennt, startet er einen Download der Ressource mit hoher Priorität, sodass sie zum Zeitpunkt der tatsächlichen Verwendung entweder bereits abgerufen oder teilweise vorhanden ist. Für Module funktioniert sie jedoch nicht.

Hier wird es knifflig. Es gibt mehrere Anmeldedatenmodi für Ressourcen. Damit ein Cache-Treffer erzielt wird, müssen sie übereinstimmen. Andernfalls wird die Ressource zweimal abgerufen. Unnötig zu sagen, dass das doppelte Abrufen schlecht ist, da es die Bandbreite des Nutzers verschwendet und ihn ohne triftigen Grund länger warten lässt.

Für <script>- und <link>-Tags können Sie den Anmeldedatenmodus mit dem Attribut crossorigin festlegen. Es stellt sich jedoch heraus, dass ein <script type="module"> ohne crossorigin-Attribut einen Anmeldedatenmodus von omit angibt, der für <link rel="preload"> nicht existiert. Das bedeutet, dass Sie das crossorigin-Attribut sowohl in <script> als auch in <link> in einen der anderen Werte ändern müssten. Das ist möglicherweise nicht ganz einfach, wenn das, was Sie vorladen möchten, eine Abhängigkeit anderer Module ist.

Außerdem ist das Abrufen der Datei nur der erste Schritt, um den Code auszuführen. Zuerst muss der Browser sie parsen und kompilieren. Idealerweise sollte dies auch im Voraus geschehen, damit der Code bei Bedarf sofort ausgeführt werden kann. V8 (die JavaScript-Engine von Chrome) parset und kompiliert Module jedoch anders als andere JavaScript-Module. <link rel="preload"> bietet keine Möglichkeit an, anzugeben, dass die geladene Datei ein Modul ist. Der Browser kann die Datei also nur laden und in den Cache legen. Sobald das Script mit einem <script type="module">-Tag geladen wurde (oder von einem anderen Modul geladen wird), wird der Code vom Browser geparst und als JavaScript-Modul kompiliert.

Kurz gesagt: Ja. Da wir einen bestimmten link-Typ für das Vorladen von Modulen haben, können wir einfaches HTML schreiben, ohne uns Gedanken über den verwendeten Anmeldedatenmodus machen zu müssen. Die Standardeinstellungen funktionieren einfach.

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

Da der Browser jetzt weiß, dass Sie ein Modul vorladen, kann er intelligent vorgehen und das Modul analysieren und kompilieren, sobald das Abrufen abgeschlossen ist, anstatt zu warten, bis es versucht wird, es auszuführen.

Unterstützte Browser

  • Chrome: 66.
  • Edge: ≤ 79
  • Firefox: 115.
  • Safari: 17.

Quelle

Aber was ist mit den Abhängigkeiten von Modulen?

Das ist eine gute Frage. Es gibt tatsächlich etwas, das in diesem Artikel nicht behandelt wurde: Rekursion.

Die <link rel="modulepreload">-Spezifikation ermöglicht optional nicht nur das Laden des angeforderten Moduls, sondern auch des gesamten Abhängigkeitsbaums. Browser müssen dies nicht tun, können es aber.

Welche plattformübergreifende Lösung eignet sich am besten zum Vorladen eines Moduls und seines Abhängigkeitsbaums, da Sie den vollständigen Abhängigkeitsbaum zum Ausführen der App benötigen?

Browser, die Abhängigkeiten rekursiv vorladen, sollten eine robuste Deduplizierung von Modulen haben. Daher empfiehlt es sich im Allgemeinen, das Modul und die flache Liste seiner Abhängigkeiten zu deklarieren und darauf zu vertrauen, dass der Browser dasselbe Modul nicht zweimal abholt.

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

Verbessert das Vorabladen von Modulen die Leistung?

Das Vorladen kann dazu beitragen, die Bandbreitennutzung zu maximieren, indem dem Browser mitgeteilt wird, was er abrufen muss, damit er während dieser langen Rücksprünge nicht untätig ist. Wenn Sie mit Modulen experimentieren und aufgrund von tiefen Abhängigkeitsbäumen Leistungsprobleme auftreten, kann das Erstellen einer flachen Liste mit Vorab-Ladungen hilfreich sein.

Die Modulleistung wird jedoch noch optimiert. Sehen Sie sich daher mit den Entwicklertools genau an, was in Ihrer Anwendung passiert, und überlegen Sie, Ihre Anwendung in der Zwischenzeit in mehrere Chunks zu bündeln. Wir arbeiten jedoch weiterhin an vielen Chrome-Modulen. Wir nähern uns also dem Tag, an dem wir die Bundles einmotten können.