Das Frontend beschleunigen. Wenn viele Serveranforderungen gut sind

Dieser Artikel beschreibt einige Methoden zum Beschleunigen des Ladens von Front-End-Anwendungen, um eine reaktionsschnelle, schnelle Benutzeroberfläche zu implementieren.

Wir werden die allgemeine Architektur des Frontends diskutieren, wie die erforderlichen Ressourcen vorgeladen werden und die Wahrscheinlichkeit erhöht wird, dass sie sich im Cache befinden. Wir werden ein wenig darüber diskutieren, wie Ressourcen aus dem Backend bereitgestellt werden und wann es möglich ist, uns auf statische Seiten anstelle einer interaktiven Clientanwendung zu beschränken.

Der Downloadvorgang ist in drei Phasen unterteilt. Für jede Phase formulieren wir allgemeine Strategien zur Steigerung der Produktivität:

  1. Erstes Rendern : Wie lange dauert es, bis der Benutzer mindestens etwas sieht?
    • Reduzieren Sie Rendering-Blockierungsanforderungen
    • Vermeiden Sie aufeinanderfolgende Ketten
    • Serververbindungen wiederverwenden
    • Servicemitarbeiter für das sofortige Rendern
  2. : ,
    • . .
    • ,


  3. :
    • ,


Bis zum ersten Rendern sieht der Benutzer nichts auf dem Bildschirm. Was brauchen wir für dieses Rendering? Laden Sie mindestens ein HTML-Dokument und in den meisten Fällen zusätzliche Ressourcen wie CSS- und JavaScript-Dateien hoch. Sobald sie verfügbar sind, kann der Browser mit dem Rendern beginnen. WebPageTest-

Diagramme werden in diesem Artikel bereitgestellt . Die Abfragesequenz für Ihre Site sieht wahrscheinlich ungefähr so ​​aus. Das HTML-Dokument lädt eine Reihe zusätzlicher Dateien und die Seite wird nach dem Herunterladen gerendert. Bitte beachten Sie, dass CSS-Dateien parallel zueinander geladen werden, sodass jede zusätzliche Anforderung keine signifikante Verzögerung verursacht. (Hinweis: Im Screenshot ist gov.uk ein Beispiel, in dem HTTP / 2 jetzt aktiviert ist





Damit die Ressourcendomäne eine vorhandene Verbindung wiederverwenden kann. Siehe unten für Serververbindungen.)

Reduzieren Sie Rendering-Blockierungsanforderungen


Stylesheets und (standardmäßig) Skripte blockieren das Rendern von Inhalten darunter.

Es gibt verschiedene Möglichkeiten, dies zu beheben:

  • Verschieben Sie Skript-Tags an den unteren Rand des Körpers
  • Laden Sie Skripte im asynchronen Modus mit herunter async
  • Wenn JS oder CSS nacheinander geladen werden sollen, ist es besser, sie mit kleinen Snippets einzubetten

Vermeiden Sie Gespräche mit sequentiellen Anforderungen, die das Rendern blockieren


Die Verzögerung beim Rendern der Site ist nicht unbedingt mit einer großen Anzahl von Anforderungen verbunden, die das Rendern blockieren. Wichtiger ist die Größe jeder Ressource sowie die Startzeit des Downloads. Dies ist der Moment, in dem der Browser plötzlich erkennt, dass diese Ressource heruntergeladen werden muss.

Wenn der Browser erkennt, dass die Datei erst nach Abschluss einer anderen Anforderung heruntergeladen werden muss, gibt es eine Reihe von Anforderungen. Es kann sich aus verschiedenen Gründen bilden:

  • @importCSS- Regeln
  • Web-Schriftarten, auf die in der CSS-Datei verwiesen wird
  • Herunterladbare JavaScript- oder Skript-Tags

Schauen Sie sich dieses Beispiel an:



Eine der CSS-Dateien auf dieser Website lädt die Google-Schriftart über die Regel @import. Dies bedeutet, dass der Browser abwechselnd die folgenden Anforderungen ausführen muss:

  1. HTML-Dokument
  2. CSS-Anwendungen
  3. CSS für Google Fonts
  4. Google Font Woff-Datei (im Diagramm nicht dargestellt)

Um dies zu beheben, verschieben Sie zuerst die Google Fonts CSS-Anforderung vom Tag @importin den Link im HTML-Dokument. Also verkürzen wir die Kette um ein Glied.

Um die Arbeit noch weiter zu beschleunigen, binden Sie Google Fonts CSS direkt in Ihre HTML- oder CSS-Datei ein.

(Beachten Sie, dass die CSS-Antwort vom Google Fonts-Server von der Benutzeragentenzeile abhängt. Wenn Sie eine Anfrage mit IE8 stellen, verweist das CSS auf die EOT-Datei, der IE11-Browser empfängt die woff-Datei und moderne Browser erhalten die woff2-Datei. Wenn Sie dem zustimmen Alte Browser sind auf Systemschriftarten beschränkt. Sie können den Inhalt der CSS-Datei einfach kopieren und in sich selbst einfügen.

Selbst nach dem Start des Renderns ist es unwahrscheinlich, dass der Benutzer mit der Seite interagieren kann, da die Schriftart geladen werden muss, um den Text anzuzeigen. Dies ist eine zusätzliche Netzwerkverzögerung, die ich vermeiden möchte. Der Swap-Parameter ist hier nützlich . Er ermöglicht es Ihnen, ihn font-displaymit Google Fonts zu verwenden und Schriftarten lokal zu speichern.

Manchmal kann die Abfragekette nicht aufgelöst werden. In solchen Fällen sollten Sie das Preload- oder Preconnect-Tag berücksichtigen . Beispielsweise kann die Website im obigen Beispiel eine Verbindung herstellen, fonts.googleapis.combevor die eigentliche CSS-Anforderung eintrifft.

Wiederverwenden von Serververbindungen, um Anforderungen zu beschleunigen


Um eine neue Verbindung zum Server herzustellen, sind normalerweise drei Paketaustausche zwischen dem Browser und dem Server erforderlich:

  1. DNS-Suche
  2. Stellen Sie eine TCP-Verbindung her
  3. Stellen Sie eine SSL-Verbindung her

Nachdem die Verbindung hergestellt wurde, ist mindestens ein weiterer Paketaustausch erforderlich, um eine Anforderung zu senden und eine Antwort zu empfangen.

Die folgende Tabelle zeigt , dass wir eine Verbindung mit vier verschiedenen Servern initiieren: hostgator.com, optimizely.com, googletagmanager.com, und googelapis.com.

Jedoch können nachfolgende Serveranfragen eine bestehende Verbindung wieder verwenden . Der Download erfolgt base.cssentweder index1.cssschneller, da sie sich auf demselben Server befinden, hostgator.commit dem bereits eine Verbindung hergestellt wurde.



Reduzieren Sie die Dateigröße und verwenden Sie CDN


Sie steuern zwei Faktoren, die sich auf die Ausführungszeit der Abfrage auswirken: die Größe der Ressourcendateien und den Speicherort der Server.

Senden Sie so wenig Daten wie möglich an den Benutzer und stellen Sie sicher, dass diese komprimiert sind (z. B. mit brotli oder gzip).

Content Delivery Networks (CDNs) verfügen über Server auf der ganzen Welt. Anstatt eine Verbindung zu einem zentralen Server herzustellen, kann ein Benutzer eine Verbindung zu einem näher gelegenen CDN-Server herstellen. Somit wird der Paketaustausch viel schneller sein. Dies ist besonders für statische Ressourcen wie CSS, JavaScript und Bilder geeignet, da diese einfach über CDN verteilt werden können.

Beseitigen Sie die Netzwerklatenz mit Servicemitarbeitern


Mit Service Workern können Sie Anforderungen abfangen, bevor Sie sie an das Netzwerk senden. Dies bedeutet, dass die Antwort fast sofort kommt !



Dies funktioniert natürlich nur, wenn Sie wirklich keine Daten vom Netzwerk empfangen müssen. Die Antwort sollte bereits zwischengespeichert sein, sodass der Vorteil erst beim zweiten Herunterladen der Anwendung angezeigt wird.

Der unten stehende Servicemitarbeiter speichert das zum Rendern der Seite erforderliche HTML und CSS zwischen. Wenn die Anwendung erneut geladen wird, versucht sie, zwischengespeicherte Ressourcen selbst auszugeben - und greift nur dann auf das Netzwerk zu, wenn sie nicht verfügbar sind.

self.addEventListener("install", async e => {
 caches.open("v1").then(function (cache) {
   return cache.addAll(["/app", "/app.css"]);
 });
});

self.addEventListener("fetch", event => {
 event.respondWith(
   caches.match(event.request).then(cachedResponse => {
     return cachedResponse || fetch(event.request);
   })
 );
});

In diesem Handbuch wird die Verwendung von Servicemitarbeitern zum Vorladen und Zwischenspeichern von Ressourcen ausführlich erläutert.

Anwendung herunterladen


Der Benutzer sieht also etwas auf dem Bildschirm. Welche weiteren Schritte sind erforderlich, damit er die Anwendung verwenden kann?

  1. Anwendungscode herunterladen (JS und CSS)
  2. Laden Sie die erforderlichen Daten für die Seite herunter
  3. Laden Sie zusätzliche Daten und Bilder herunter



Bitte beachten Sie, dass nicht nur das Herunterladen von Daten aus dem Netzwerk das Rendern verzögern kann. Sobald Ihr Code geladen ist, sollte der Browser ihn analysieren, kompilieren und ausführen.

Laden Sie nur den erforderlichen Code herunter und maximieren Sie die Anzahl der Treffer im Cache


"Paket brechen" bedeutet, dass nur der Code heruntergeladen wird, der für die aktuelle Seite benötigt wird, nicht die gesamte Anwendung. Dies bedeutet auch, dass Teile des Pakets zwischengespeichert werden können, auch wenn andere Teile geändert wurden und neu geladen werden müssen.

Der Code ist in der Regel in folgende Teile unterteilt:

  • Code für eine bestimmte Seite (seitenspezifisch)
  • Gemeinsamer Anwendungscode
  • Module von Drittanbietern, die sich selten ändern (ideal zum Zwischenspeichern!)

Webpack kann diese Optimierung automatisch durchführen, den Code brechen und das Gesamtladegewicht reduzieren. Der Code wird mithilfe des Objekts Optimization.splitChunks in Teile zerlegt . Trennen Sie die Laufzeit (Laufzeit) in eine separate Datei: Auf diese Weise können Sie vom langfristigen Caching profitieren. Ivan Akulov hat eine detaillierte Anleitung zum Aufteilen eines Pakets in separate Dateien und zum Zwischenspeichern in Webpack geschrieben .

Es ist nicht möglich, Code für eine bestimmte Seite automatisch zuzuweisen. Sie müssen die Teile, die separat heruntergeladen werden können, manuell identifizieren. Oft ist dies ein bestimmter Pfad oder eine Reihe von Seiten. Verwenden Sie dynamische Importe , um diesen Code träge zu laden.

Durch die Aufteilung des Gesamtpakets in Teile wird die Anzahl der Anfragen zum Herunterladen Ihrer Anwendung erhöht. Dies ist jedoch kein großes Problem, wenn Anforderungen parallel ausgeführt werden, insbesondere wenn die Site mithilfe des HTTP / 2-Protokolls geladen wird. Sie können dies für die ersten drei Abfragen im folgenden Diagramm sehen: Im Diagramm sind



jedoch auch zwei aufeinanderfolgende Abfragen sichtbar. Diese Fragmente werden nur für diese bestimmte Seite benötigt und dynamisch durch geladen import().

Sie können versuchen, das Problem zu beheben, indem Sie ein Tag-Preload- Preload einfügen .



Wir sehen jedoch, dass sich die Gesamtladezeit der Seite erhöht hat.

Das Vorladen von Ressourcen ist manchmal kontraproduktiv, da es das Laden wichtigerer Dateien verzögert. LesenAndy Davis 'Artikel über das Vorladen von Schriftarten und wie dieses Verfahren den Start des Seiten-Renderings blockiert.

Laden von Daten für eine Seite


Ihre Anwendung sollte wahrscheinlich einige Daten anzeigen. Hier sind einige Tipps, mit denen Sie diese Daten ohne unnötige Verzögerungen beim Rendern frühzeitig herunterladen können.

Warten Sie nicht auf den vollständigen Download des Pakets, bevor Sie mit dem Herunterladen der Daten beginnen


Hier ist ein Sonderfall einer Kette von sequentiellen Anforderungen: Sie laden das gesamte Anwendungspaket herunter, und dieser Code fordert dann die erforderlichen Daten für die Seite an.

Es gibt zwei Möglichkeiten, dies zu vermeiden:

  1. Daten in ein HTML-Dokument einbetten
  2. Führen Sie eine Datenanforderung mit dem im Dokument integrierten Skript aus

Durch das Einbetten der Daten in HTML wird sichergestellt, dass die Anwendung nicht auf das Laden wartet. Dies reduziert auch die Komplexität des Systems, da Sie den Startstatus nicht verarbeiten müssen.

Dies ist jedoch keine gute Idee, wenn eine solche Technik das anfängliche Rendern verzögert.

In diesem Fall können Sie diese Daten alternativ auch über das integrierte Skript herunterladen, wenn Sie ein zwischengespeichertes HTML-Dokument über einen Servicemitarbeiter senden. Sie können es als globales Objekt verfügbar machen. Hier ist ein Versprechen:

window.userDataPromise = fetch("/me")

Wenn die Daten bereit sind und in einer solchen Situation, kann die Anwendung sofort mit dem Rendern beginnen oder warten, bis sie bereit ist.

Wenn Sie beide Methoden verwenden, müssen Sie im Voraus wissen, welche Daten auf der Seite geladen werden, bevor die Anwendung mit dem Rendern beginnt. Dies ist normalerweise für benutzerbezogene Daten (Benutzername, Benachrichtigungen usw.) offensichtlich, jedoch schwieriger für Inhalte, die für eine bestimmte Seite spezifisch sind. Vielleicht ist es sinnvoll, die wichtigsten Seiten hervorzuheben und eine eigene Logik dafür zu schreiben.

Blockieren Sie das Rendern nicht, während Sie auf irrelevante Daten warten


Manchmal müssen Sie zum Generieren von Daten eine langsame, komplexe Logik im Backend ausführen. In solchen Fällen können Sie zunächst versuchen, eine einfachere Version der Daten herunterzuladen, wenn dies ausreicht, um die Anwendung funktionsfähig und interaktiv zu machen.

Beispielsweise kann ein Analysetool zuerst eine Liste aller Diagramme herunterladen, bevor Daten geladen werden. Auf diese Weise kann der Benutzer sofort nach dem für ihn interessanten Diagramm suchen und Backend-Anforderungen an verschiedene Server verteilen.



Vermeiden Sie aufeinanderfolgende Datenabfragen


Dies kann dem vorherigen Absatz widersprechen, dass es besser ist, nicht wesentliche Daten in einer separaten Anfrage anzugeben. Daher sollte klargestellt werden: Vermeiden Sie Ketten mit sequentiellen Datenanforderungen, wenn jede abgeschlossene Anforderung nicht dazu führt, dass dem Benutzer mehr Informationen angezeigt werden .

Anstatt zuerst abzufragen, welcher Benutzer angemeldet ist, und dann eine Liste seiner Gruppen anzufordern, geben Sie sofort die Liste der Gruppen zusammen mit den Benutzerinformationen in der ersten Abfrage zurück. Sie können hierfür GraphQL verwenden , aber der Endpunkt user?includeTeams=truefunktioniert auch einwandfrei .

Serverseitiges Rendern


Serverseitiges Rendern bedeutet, dass die Anwendung vorab gerendert wird, sodass auf Clientanforderung ein ganzseitiger HTML-Code zurückgegeben wird. Der Client sieht die Seite vollständig gerendert, ohne auf das Laden von zusätzlichem Code oder Daten zu warten!

Da der Server dem Client nur statisches HTML sendet, ist die Anwendung zu diesem Zeitpunkt nicht interaktiv. Sie müssen die Anwendung selbst herunterladen, die Renderlogik starten und dann die erforderlichen Ereignis-Listener mit dem DOM verbinden.

Verwenden Sie serverseitiges Rendering, wenn das Anzeigen nicht interaktiver Inhalte an und für sich wertvoll ist. Es ist auch schön, gerendertes HTML auf dem Server zwischenzuspeichern und es unverzüglich an alle Benutzer zurückzugeben. Beispielsweise eignet sich das serverseitige Rendern hervorragend, wenn Sie React zum Anzeigen von Blog-Posts verwenden.

BEIMDieser Artikel von Mikhail Yanashek beschreibt, wie Servicemitarbeiter und serverseitiges Rendering kombiniert werden.

Nächste Seite


Irgendwann drückt der Benutzer eine Taste und geht zur nächsten Seite. Ab dem Moment, in dem Sie die Startseite öffnen, steuern Sie, was im Browser geschieht, damit Sie sich auf die nächste Interaktion vorbereiten können.

Vorladen von Ressourcen


Wenn Sie den für die nächste Seite erforderlichen Code vorladen, verschwindet die Verzögerung, wenn der Benutzer mit der Navigation beginnt. Verwenden Sie Prefetch-Tags oder webpackPrefetchfür den dynamischen Import:

import(
    /* webpackPrefetch: true, webpackChunkName: "todo-list" */ "./TodoList"
)

Überlegen Sie, welche Art von Last Sie dem Benutzer in Bezug auf Datenverkehr und Bandbreite auferlegen, insbesondere wenn er über eine mobile Verbindung verbunden ist. Wenn eine Person die mobile Version der Site heruntergeladen hat und der Datenspeichermodus aktiv ist, ist es sinnvoll, weniger aggressiv vorzuladen.

Überlegen Sie strategisch, welche Teile der Anwendung der Benutzer zuvor benötigen wird.

Wiederverwendung bereits heruntergeladener Daten


Zwischenspeichern Sie Daten lokal in Ihrer Anwendung und verwenden Sie sie, um zukünftige Anforderungen zu vermeiden. Wenn der Benutzer von der Liste seiner Gruppen zur Seite "Gruppe bearbeiten" wechselt, können Sie den Übergang sofort vornehmen und zuvor heruntergeladene Daten über die Gruppe wiederverwenden.

Bitte beachten Sie, dass dies nicht funktioniert, wenn das Objekt häufig von anderen Benutzern bearbeitet wird und die heruntergeladenen Daten möglicherweise veraltet sind. In diesen Fällen besteht die Möglichkeit, zuerst vorhandene schreibgeschützte Daten anzuzeigen und gleichzeitig eine Anforderung für aktualisierte Daten auszuführen.

Fazit


Dieser Artikel listet eine Reihe von Faktoren auf, die Ihre Seite in verschiedenen Phasen des Ladevorgangs verlangsamen können. Mithilfe von Tools wie Chrome DevTools , WebPageTest und Lighthouse können Sie herausfinden, welche dieser Faktoren Ihre Anwendung beeinflussen.

In der Praxis geht die Optimierung selten sofort in alle Richtungen. Wir müssen herausfinden, was die größten Auswirkungen auf die Benutzer hat, und uns darauf konzentrieren.

Während ich den Artikel schrieb, wurde mir eines wichtig: Ich war fest davon überzeugt, dass viele einzelne Serveranforderungen die Leistung beeinträchtigen. Dies war in der Vergangenheit der Fall, als für jede Anforderung eine separate Verbindung erforderlich war und Browser nur wenige Verbindungen pro Domäne zuließen. Bei HTTP / 2 und modernen Browsern ist dies jedoch nicht mehr der Fall.

Es gibt gute Argumente für die Aufteilung der Anwendung in Teile (mit multiplizierenden Abfragen). Auf diese Weise können Sie nur die erforderlichen Ressourcen herunterladen und den Cache besser verwenden, da nur die geänderten Dateien neu geladen werden müssen.

All Articles