JavaScript für Codeaufteilung

Das Laden großer JavaScript-Ressourcen beeinträchtigt die Seitengeschwindigkeit erheblich. Wird aufgeteilt in kleinere Teile aufteilen und nur das herunterladen, Eine Seite, die beim Start funktioniert, kann die Ladegeschwindigkeit Ihrer Seite erheblich verbessern. Reaktionszeit, was sich wiederum positiv auf die Interaktion mit dem nächsten Video Paint (INP):

Wenn eine Seite große JavaScript-Dateien herunterlädt, analysiert und kompiliert, über einen bestimmten Zeitraum nicht mehr reagieren. Die Seitenelemente sind sichtbar, da sie Teil des anfänglichen HTML-Codes einer Seite und mit CSS formatiert ist. Da JavaScript jedoch die zum Betreiben dieser interaktiven Elemente erforderlich sind, sowie anderer Skripts, die von parst und führt sie JavaScript aus, damit sie funktionieren. Die Das Ergebnis ist, dass die Nutzenden das Gefühl haben, dass die Interaktion verzögert oder sogar völlig kaputt ist.

Dies passiert häufig, weil der Hauptthread beim Parsen von JavaScript blockiert wird. und im Hauptthread kompiliert. Wenn dieser Vorgang zu lange dauert, Seitenelemente möglicherweise nicht schnell genug auf Nutzereingaben reagieren. Eine Abhilfe in diesem Fall nur den JavaScript-Code zu laden, der für das Funktionieren der Seite erforderlich ist, während Verzögern des Ladens anderer JavaScript-Elemente mithilfe einer Technik, die als Code bezeichnet wird die Aufteilung. In diesem Modul konzentrieren wir uns auf die letztere dieser beiden Methoden.

JavaScript-Parsing und -Ausführung beim Start durch Codeaufteilung reduzieren

Lighthouse gibt eine Warnung aus, wenn die Ausführung von JavaScript länger als zwei Sekunden und schlägt fehl, wenn es länger als 3,5 Sekunden dauert. Übermäßiges JavaScript das Parsen und die Ausführung stellen an jeder Stelle auf der Seite ein potenzielles Problem dar Lebenszyklus, da er die Eingabeverzögerung einer Interaktion erhöhen kann. wenn der Zeitpunkt, zu dem der Nutzer mit der Seite interagiert, mit dem Moment übereinstimmt. sind die Hauptthreadaufgaben, die für die Verarbeitung und Ausführung von JavaScript verantwortlich sind, ausgeführt wird.

Darüber hinaus ist übermäßiges Ausführen und Parsen von JavaScript beim ersten Seitenaufbau problematisch, da dies der Punkt auf der Seite ist, dass Nutzer mit hoher Wahrscheinlichkeit mit der Seite interagieren. Tatsächlich Die Gesamtblockierungszeit (Total Blocking Time, TBT), ein Messwert für die Reaktionsfähigkeit bei Lasten, steht in einer starken Korrelation mit INP, was darauf hindeutet, dass Nutzer*innen eine hohe Tendenz zu Interaktionen haben beim ersten Seitenaufbau an.

Die Lighthouse-Prüfung, in der der Zeitaufwand für die Ausführung der einzelnen JavaScript-Dateien erfasst wird dass Ihre Seitenanforderungen nützlich sind, da Sie so genau ermitteln können, Scripts eignen sich möglicherweise für die Codeaufteilung. Sie können dann weiter gehen, indem Sie mit dem Tool für die Abdeckung in den Chrome-Entwicklertools, um genau zu ermitteln, das JavaScript einer Seite beim Laden der Seite nicht verwendet wird.

Codeaufteilung ist eine nützliche Technik, mit der die anfängliche JavaScript-Größe einer Seite reduziert werden kann. Payloads. Damit können Sie ein JavaScript-Bundle in zwei Teile aufteilen:

  • Entspricht dem JavaScript-Code, der beim Seitenaufbau benötigt wird und daher nicht auf anderen .
  • Verbleibendes JavaScript, das meistens zu einem späteren Zeitpunkt geladen werden kann der Punkt, an dem die Nutzenden mit einem bestimmten interaktiven Element auf auf der Seite.

Codeaufteilung kann mithilfe der dynamischen import()-Syntax erfolgen. Dieses Syntax (im Gegensatz zu <script>-Elementen, die eine bestimmte JavaScript-Ressource anfordern). beim Start: Eine JavaScript-Ressource wird zu einem späteren Zeitpunkt während Seitenlebenszyklus.

<ph type="x-smartling-placeholder">
document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

Im vorherigen JavaScript-Snippet ist das Modul validate-form.mjs nur dann heruntergeladen, geparst und ausgeführt, wenn ein Nutzer eines der Elemente eines Formulars unkenntlich gemacht hat. <input>-Felder. In diesem Fall kann die JavaScript-Ressource, Die Validierungslogik des Formulars spielt nur dann eine Rolle, tatsächlich verwendet wird.

JavaScript-Bundler wie webpack, Parcel, Rollup und esbuild können JavaScript-Bundles immer in kleinere Blöcke aufzuteilen, dass ein dynamischer Aufruf von import() in Ihrem Quellcode angezeigt wird. Die meisten dieser Tools Dies geschieht automatisch, aber insbesondere für Optimierung.

<ph type="x-smartling-placeholder">

Hilfreiche Hinweise zum Aufteilen von Code

Codeaufteilung ist zwar eine effektive Methode, um Konflikte im Hauptthread zu reduzieren Beim ersten Seitenaufbau zahlt es sich aus, einige Dinge zu beachten, wenn Sie entscheiden, um Ihren JavaScript-Quellcode auf Möglichkeiten zur Codeaufteilung zu überprüfen.

Verwenden Sie, wenn möglich, einen Bundler.

Es ist üblich, dass Entwickler JavaScript-Module während der und Entwicklungsprozessen. Es ist eine hervorragende Verbesserung der Entwicklererfahrung, verbessert die Lesbarkeit und Verwaltbarkeit des Codes. Es gibt jedoch einige suboptimale Leistungsmerkmale, die sich beim Versand von JavaScript bis hin zur Produktion.

Am wichtigsten ist, dass Sie einen Bundler zur Verarbeitung und Optimierung Ihrer Quelle verwenden. und Module, die Sie aufteilen möchten. Bundler sind sehr effektiv, Optimierungen werden nicht nur auf den JavaScript-Quellcode angewendet, sondern sind auch ist sehr effektiv bei der Abwägung von Leistungsaspekten wie der Bundle-Größe. gegenüber dem Verdichtungsverhältnis. Die Effektivität der Komprimierung erhöht sich mit der Bundle-Größe, Bundler achten aber auch darauf, dass Pakete nicht so groß sind, zu langen Aufgaben aufgrund der Skriptauswertung.

Bundler vermeiden außerdem das Problem, dass eine große Anzahl entbündelter Module versendet wird. über das Netzwerk. Architekturen, die JavaScript-Module verwenden, haben meist große, und komplexe Modulbäume. Bei der Entbündelung von Modulbäumen stellt jedes Modul ein HTTP-Anfrage getrennt. Die Interaktivität in Ihrer Webanwendung kann sich verzögern, bündeln Sie keine Module. Mit der Funktion <link rel="modulepreload">-Ressourcenhinweis zum frühzeitigen Laden großer Modulstrukturen JavaScript-Bundles immer noch gegenüber Ladeleistungen vorzuziehen, Standpunkt.

Deaktiviere nicht versehentlich die Streaming-Kompilierung

Die V8-JavaScript-Engine von Chromium bietet eine Reihe standardmäßiger Optimierungen. damit Ihr Produktions-JavaScript-Code so effizient wie möglich geladen wird. Eine dieser Optimierungen wird als Streaming-Kompilierung bezeichnet. inkrementelles Parsen von HTML-Code, der an den Browser gestreamt wird – kompiliert gestreamte Blöcke wenn sie vom Netzwerk empfangen werden.

Sie haben mehrere Möglichkeiten, sicherzustellen, dass eine Streaming-Kompilierung für Ihr Konto erfolgt. Webanwendung in Chromium:

  • Transformieren Sie Ihren Produktionscode, um JavaScript-Module zu vermeiden. Bündel Ihren JavaScript-Quellcode basierend auf einem Kompilierungsziel umwandeln und ist das Ziel oft umgebungsspezifisch. V8 wendet Streaming an Kompilierung in JavaScript-Code, der keine Module verwendet, und Sie können Bundler so konfigurieren, dass der Code des JavaScript-Moduls in eine Syntax umgewandelt wird der keine JavaScript-Module nutzt.
  • Wenn Sie JavaScript-Module an die Produktion senden möchten, verwenden Sie die .mjs Erweiterung. Unabhängig davon, ob Ihr Produktions-JavaScript Module verwendet oder nicht, Kein spezieller Inhaltstyp für JavaScript, der Module statt JavaScript verwendet aber das ist nicht der Fall. Bei V8 wird das Streaming effektiv deaktiviert. Kompilierung, wenn du JavaScript-Module in der Produktion mit der .js sendest . Wenn Sie die Erweiterung .mjs für JavaScript-Module verwenden, kann V8 dass die Streaming-Kompilierung für modulbasierten JavaScript-Code kaputt.

Lassen Sie sich von diesen Überlegungen nicht davon abhalten, Codeaufteilung zu verwenden. Code ist eine effektive Methode, um die anfängliche JavaScript-Nutzlast für Nutzende zu reduzieren. Wenn Sie einen Bundler verwenden und wissen, wie Sie das Streaming von V8 Kompilierungsverhalten haben, können Sie sicherstellen, dass Ihr JavaScript-Produktionscode genauso so schnell für die Nutzenden, wie es sein kann.

Dynamischer Import – Demo

Webpack

webpack wird mit dem Plug-in SplitChunksPlugin ausgeliefert, mit dem Sie konfigurieren, wie der Bundler JavaScript-Dateien aufteilt. erkennt Webpack sowohl die dynamische import()- und statische import-Anweisungen. Das Verhalten von SplitChunksPlugin kann geändert werden, indem die Option chunks in der zugehörigen Konfiguration:

  • chunks: async ist der Standardwert und bezieht sich auf dynamische import()-Aufrufe.
  • chunks: initial bezieht sich auf statische import-Aufrufe.
  • chunks: all deckt sowohl dynamische import()- als auch statische Importe ab, sodass Sie um Blöcke für async- und initial-Importe freizugeben.

Standardmäßig immer dann, wenn Webpack auf eine dynamische import()-Anweisung stößt. sie einen separaten Chunk für dieses Modul:

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

Die Standard-Webpack-Konfiguration für das vorherige Code-Snippet führt zu zwei separate Blöcke:

  • Dem main.js-Chunk, der von Webpack als initial-Block klassifiziert wird, enthält das Modul main.js und ./my-function.js.
  • Der Block async, der nur form-validation.js enthält (mit einem Datei-Hash im Ressourcennamen, falls konfiguriert. Dieser Block wird nur heruntergeladen wenn condition wahrheit ist.

Mit dieser Konfiguration können Sie das Laden des Blocks form-validation.js verzögern, bis tatsächlich benötigt wird. Dies kann die Reaktionsfähigkeit beim Laden verbessern, indem das Script Bewertungszeit beim ersten Seitenaufbau an. Skriptdownload und -auswertung für den Block form-validation.js tritt auf, wenn eine bestimmte Bedingung erfüllt ist, in In diesem Fall wird das dynamisch importierte Modul heruntergeladen. Ein Beispiel könnte ein wenn ein Polyfill nur für einen bestimmten Browser heruntergeladen wird. aus dem vorherigen Beispiel: Das importierte Modul ist für eine Nutzerinteraktion erforderlich.

Wenn Sie jedoch die SplitChunksPlugin-Konfiguration ändern, um Mit chunks: initial wird sichergestellt, dass der Code nur auf die ersten Blöcke aufgeteilt wird. Dies sind Blöcke wie statisch importierte oder in der Datei entry des Webpacks aufgelistete Blöcke . Im vorherigen Beispiel wäre der resultierende Chunk ein Kombination aus form-validation.js und main.js in einer Skriptdatei Dies kann die Leistung beim ersten Seitenaufbau verschlechtern.

Die Optionen für SplitChunksPlugin können auch so konfiguriert werden, dass größere Skripts in mehrere kleinere aufteilen, z. B. mit der Option maxSize , um Webpack anzuweisen, Blöcke in separate Dateien aufzuteilen, wenn diese die angegeben durch maxSize. Das Unterteilen großer Skriptdateien in kleinere Dateien Verbessern der Lastenreaktion, z. B. in einigen Fällen CPU-intensiver Skriptauswertung in kleinere Aufgaben aufgeteilt ist, bei denen die Hauptaufgabe für längere Zeit verwenden.

Größere JavaScript-Dateien bedeuten auch, dass Skripts ist die Wahrscheinlichkeit einer Cache-Entwertung höher. Wenn Sie zum Beispiel eine das Framework und eigenen Anwendungscode enthält, Das Bundle kann ungültig werden, wenn nur das Framework aktualisiert wird, aber nichts anderes in der gebündelten Ressource.

Andererseits erhöhen kleinere Skriptdateien die Wahrscheinlichkeit, ruft der Besucher Ressourcen aus dem Cache ab, was zu einem schnelleren Laden der Seiten auf wiederholte Besuche. Kleinere Dateien profitieren jedoch weniger von der Komprimierung als größere und kann die Netzwerk-Umlaufzeit beim Laden von Seiten mit einer Browser-Cache. Es ist wichtig, ein Gleichgewicht zwischen dem Caching Effizienz, Komprimierung und die Zeit der Skriptauswertung.

<ph type="x-smartling-placeholder">

Webpack-Demo

<ph type="x-smartling-placeholder">

Webpack SplitChunksPlugin-Demo

Wissen testen

Der Typ der import-Anweisung, die beim Ausführen von Code verwendet wird Aufteilung?

Dynamische import().
Statisches import.

Welche Art von import-Anweisung muss sich ganz oben befinden eines JavaScript-Moduls und an keiner anderen Stelle?

Statisches import.
Dynamische import().

Was ist bei der Verwendung von SplitChunksPlugin in Webpack zwischen einem async-Chunk und einem Block initial?

async Chunks werden mit dem statischen import geladen und initial-Blöcke werden mit dynamischen import().
async Blöcke werden mit dem dynamischen import() geladen und initial-Blöcke werden mithilfe einer statischen import.

Nächster Schritt: Lazy Loading von Bildern und <iframe>-Elementen

Auch wenn es sich um einen ziemlich teuren Ressourcentyp handelt, ist JavaScript nicht Ressourcentyp, dessen Laden Sie verschieben können. Bild und <iframe>-Elemente sind für sich genommen möglicherweise kostspielige Ressourcen. Ähnlich wie bei JavaScript können Sie kann das Laden von Bildern und <iframe>-Elementen durch Lazy Loading verzögern. , was im nächsten Modul dieses Kurses erklärt wird.