Das Rendern von HTML mit JavaScript unterscheidet sich vom Rendering von HTML, das vom Server gesendet wird. Dies kann sich auf die Leistung auswirken. In diesem Leitfaden erfahren Sie mehr über den Unterschied und darüber, wie Sie die Rendering-Leistung Ihrer Website beibehalten können – insbesondere, wenn es um Interaktionen geht.
Das Parsen und Rendern von HTML ist etwas, das Browser standardmäßig bei Websites, die die integrierte Navigationslogik des Browsers verwenden, – manchmal auch als „herkömmliches Laden von Seiten“ oder „harte Navigation“ bezeichnet – sehr gut funktionieren. Solche Websites werden auch als Multi-Page-Anwendungen (MPAs) bezeichnet.
Entwickler können Browser-Standardeinstellungen jedoch an die Anforderungen ihrer Anwendung anpassen. Dies trifft sicher auf Websites zu, die das Single-Page Application-Muster (SPA) verwenden, bei dem große Teile des HTML/DOM auf dem Client mit JavaScript dynamisch erstellt werden. Clientseitiges Rendering ist der Name für dieses Designmuster. Es kann Auswirkungen auf die Interaction to Next Paint (INP) Ihrer Website haben, wenn der damit verbundene Aufwand übermäßig ist.
Mithilfe dieses Leitfadens können Sie den Unterschied zwischen der Verwendung von HTML, das vom Server an den Browser gesendet wird, und dessen Erstellung auf dem Client mit JavaScript abwägen, und wie Letzteres in entscheidenden Momenten zu einer hohen Interaktionslatenz führen kann.
So rendert der Browser den vom Server bereitgestellten HTML-Code
Das beim herkömmlichen Laden von Seiten verwendete Navigationsmuster beinhaltet den Empfang von HTML-Code vom Server bei jeder Navigation. Wenn Sie eine URL in die Adressleiste Ihres Browsers eingeben oder auf einen Link in einer MPA-Datei klicken, geschieht Folgendes:
- Der Browser sendet eine Navigationsanfrage für die angegebene URL.
- Der Server antwortet mit HTML in Abschnitten.
Der letzte Schritt hier ist entscheidend. Es ist auch eine der grundlegendsten Leistungsoptimierungen beim Server/Browser-Austausch und wird als Streaming bezeichnet. Wenn der Server so bald wie möglich mit dem Senden von HTML beginnen kann und der Browser nicht auf das Eintreffen der gesamten Antwort wartet, kann der Browser den HTML-Code beim Eintreffen in Blöcken verarbeiten.
Wie bei den meisten Vorgängen im Browser erfolgt das Parsen von HTML innerhalb von Tasks. Wenn HTML vom Server zum Browser gestreamt wird, optimiert der Browser das Parsen dieses HTML-Codes, indem er dies Bit für Stück erfolgt, wenn Bits des Streams stückweise eingehen. Dies hat zur Folge, dass der Browser nach der Verarbeitung jedes Blocks regelmäßig an den Hauptthread übergibt. Dadurch werden lange Aufgaben vermieden. Das bedeutet, dass während des Parsens von HTML weitere Vorgänge stattfinden können, einschließlich des inkrementellen Renderings, das erforderlich ist, um dem Nutzer eine Seite zu präsentieren, sowie der Verarbeitung von Nutzerinteraktionen, die während der wichtigen Startphase der Seite auftreten können. Dieser Ansatz führt zu einer besseren Interaction to Next Paint (INP)-Wertung für die Seite.
Das bedeutet: Wenn Sie HTML-Inhalte vom Server streamen, können Sie den HTML-Code schrittweise analysieren und rendern und die Ergebnisse automatisch und kostenlos an den Hauptthread senden. Beim clientseitigen Rendering ist das nicht möglich.
So rendert der Browser von JavaScript bereitgestellten HTML-Code
Während für jede Navigationsanforderung an eine Seite eine gewisse Menge an HTML-Code vom Server bereitgestellt werden muss, verwenden einige Websites das SPA-Muster. Bei diesem Ansatz wird häufig eine minimale HTML-Anfangsnutzlast vom Server bereitgestellt, aber dann füllt der Client den Hauptinhaltsbereich einer Seite mit HTML aus, das aus den vom Server abgerufenen Daten zusammengestellt wird. Die nachfolgenden Navigationen – in diesem Fall auch als „weiche Navigationen“ bezeichnet – werden vollständig von JavaScript verarbeitet, um die Seite mit neuem HTML-Code zu füllen.
In bestimmten Fällen, in denen HTML über JavaScript dynamisch zum DOM hinzugefügt wird, kann das clientseitige Rendering auch bei Nicht-SPAs erfolgen.
Es gibt einige gängige Methoden, um HTML zu erstellen oder das DOM über JavaScript hinzuzufügen:
- Mit der
innerHTML
-Eigenschaft können Sie den Inhalt eines vorhandenen Elements über einen String festlegen, den der Browser in das DOM analysiert. - Mit der Methode
document.createElement
können Sie neue Elemente erstellen, die dem DOM hinzugefügt werden, ohne dass ein Browser-HTML-Parsing erforderlich ist. - Mit der
document.write
-Methode können Sie HTML-Code in das Dokument schreiben. Der Browser parst diesen dann wie bei Methode 1. Aus mehreren Gründen wird jedoch dringend von der Verwendung vondocument.write
abgeraten.
Die Erstellung von HTML/DOM über clientseitiges JavaScript kann erhebliche Konsequenzen haben:
- Im Gegensatz zu HTML, das vom Server als Antwort auf eine Navigationsanfrage gestreamt wird, werden JavaScript-Aufgaben auf dem Client nicht automatisch aufgeteilt, was zu langen Aufgaben führen kann, die den Hauptthread blockieren. Das bedeutet, dass die INP Ihrer Seite negativ beeinflusst werden kann, wenn Sie zu viel HTML/DOM gleichzeitig im Client erstellen.
- Wenn HTML beim Start auf dem Client erstellt wird, werden darin referenzierte Ressourcen nicht vom Browser Preload Scanner erkannt. Dies wirkt sich sicherlich negativ auf den Largest Contentful Paint (LCP) einer Seite aus. Hierbei handelt es sich zwar nicht um ein Problem mit der Laufzeitleistung, sondern um eine Netzwerkverzögerung beim Abrufen wichtiger Ressourcen. Sie möchten aber nicht, dass der LCP Ihrer Website durch eine Umgehung dieser grundlegenden Optimierung der Browserleistung beeinträchtigt wird.
Auswirkungen des clientseitigen Renderings auf die Leistung
Wenn Ihre Website stark vom clientseitigen Rendering abhängt und Sie schlechte INP-Werte in Ihren Felddaten festgestellt haben, fragen Sie sich vielleicht, ob das clientseitige Rendering mit dem Problem zusammenhängt. Handelt es sich bei Ihrer Website beispielsweise um eine SPA, können Ihre Felddaten auf Interaktionen hinweisen, die für erhebliche Rendering-Arbeiten verantwortlich sind.
Unabhängig von der Ursache haben wir hier einige mögliche Ursachen zusammengestellt, mit denen du die Probleme wieder auf den richtigen Weg bringen kannst.
Möglichst viel HTML-Code vom Server bereitstellen
Wie bereits erwähnt, verarbeitet der Browser HTML vom Server standardmäßig sehr leistungsfähig. Sie trennt das Parsen und Rendern von HTML so, dass lange Aufgaben vermieden werden und dass die Gesamtzeit des Hauptthreads optimiert wird. Dies führt zu einer niedrigeren Total Blocking Time (TBT) und die TBT korreliert stark mit INP.
Möglicherweise verlassen Sie sich beim Erstellen Ihrer Website auf ein Front-End-Framework. In diesem Fall sollten Sie überprüfen, ob Sie den HTML-Code der Komponente auf dem Server rendern. Dadurch wird der erforderliche Aufwand für das erste clientseitige Rendering Ihrer Website begrenzt, was zu einer besseren Nutzerfreundlichkeit führen sollte.
- Für React sollten Sie die Server DOM API nutzen, um HTML auf dem Server zu rendern. Bei der herkömmlichen Methode des serverseitigen Renderings wird ein synchroner Ansatz verwendet, was zu einer längeren Zeit bis zum ersten Byte (TTFB) sowie zu nachfolgenden Messwerten wie First Contentful Paint (FCP) und LCP führen kann. Verwenden Sie nach Möglichkeit die Streaming-APIs für Node.js oder andere JavaScript-Laufzeiten, damit der Server so schnell wie möglich mit dem Streamen von HTML an den Browser beginnen kann. Next.js-Framework bietet standardmäßig viele Best Practices. Er kann nicht nur HTML automatisch auf dem Server rendern, sondern auch statisch HTML für Seiten generieren, die sich nicht je nach Nutzerkontext (z. B. Authentifizierung) ändern.
- Vue führt standardmäßig auch clientseitiges Rendering aus. Wie React kann Vue jedoch auch den HTML-Code der Komponente auf dem Server rendern. Nutzen Sie nach Möglichkeit entweder diese serverseitigen APIs oder ziehen Sie eine allgemeine Abstraktion für Ihr Vue-Projekt in Betracht, um die Best Practices einfacher zu implementieren.
- Mit Svelte wird standardmäßig HTML auf dem Server gerendert. Wenn Ihr Komponentencode jedoch Zugriff auf Namespaces benötigt, die ausschließlich im Browser verfügbar sind (z. B.
window
), können Sie den HTML-Code dieser Komponente möglicherweise nicht auf dem Server rendern. Erkunden Sie nach Möglichkeit alternative Ansätze, damit kein unnötiges clientseitiges Rendering verursacht wird. Mit SvelteKit – „Svelte as Next.js“ zu React), werden nach Möglichkeit viele Best Practices in Ihre Svelte-Projekte eingebettet. So lassen sich mögliche Stolpersteine bei Projekten vermeiden, in denen nur Svelte verwendet wird.
Anzahl der auf dem Client erstellten DOM-Knoten begrenzen
Bei großen DOMs nimmt die Verarbeitungszeit für ihr Rendering tendenziell zu. Unabhängig davon, ob Ihre Website eine vollwertige SPA ist oder neue Knoten in ein vorhandenes DOM als Ergebnis einer Interaktion für eine MPA eingefügt werden, sollten Sie diese DOMs so klein wie möglich halten. Dadurch wird der Arbeitsaufwand beim clientseitigen Rendern für die Anzeige dieses HTML-Codes reduziert, was hoffentlich dazu beiträgt, den INP-Wert Ihrer Website zu senken.
Betrachten Sie eine Streaming Service Worker-Architektur
Dies ist eine fortgeschrittene Technik, die möglicherweise nicht für jeden Anwendungsfall problemlos funktioniert. Sie kann jedoch Ihre MPA in eine Website verwandeln, die das Gefühl hat, sie wird sofort geladen, wenn Nutzende von einer Seite zur nächsten navigieren. Du kannst einen Service Worker verwenden, um die statischen Teile deiner Website in CacheStorage
vorab im Cache zu speichern. Mit der ReadableStream
API kannst du den restlichen HTML-Code einer Seite vom Server abrufen.
Wenn Sie diese Technik erfolgreich einsetzen, erstellen Sie keinen HTML-Code auf dem Client, aber das sofortige Laden von Inhaltsteilen aus dem Cache vermittelt den Eindruck, dass Ihre Website schnell geladen wird. Websites mit diesem Ansatz können fast wie eine SPA wirken, aber ohne die Nachteile des clientseitigen Renderings. Außerdem wird dadurch die Menge an HTML reduziert, die Sie vom Server anfordern.
Kurz gesagt: Eine Streaming Service Worker-Architektur ersetzt nicht die integrierte Navigationslogik des Browsers, sondern ergänzt sie. Weitere Informationen dazu, wie Sie dies mit Workbox erreichen, finden Sie unter Schnellere mehrseitige Anwendungen mit Streams.
Fazit
Die Art und Weise, wie HTML HTML empfängt und rendert, wirkt sich auf die Leistung aus. Wenn Sie sich darauf verlassen, dass der Server den gesamten (oder den Großteil) des für das Funktionieren Ihrer Website erforderlichen HTML-Codes sendet, erhalten Sie eine Menge kostenlos: inkrementelles Parsing und Rendering sowie eine automatische Ausgabe an den Hauptthread, um langwierige Aufgaben zu vermeiden.
Das clientseitige HTML-Rendering bringt eine Reihe potenzieller Leistungsprobleme mit sich, die in vielen Fällen vermieden werden können. Aufgrund der individuellen Anforderungen jeder einzelnen Website kann dies jedoch nicht zu 100% vermieden werden. Um die potenziellen langen Aufgaben, die sich aus einem übermäßigen Rendering der Client-Website ergeben können, abzuschwächen, sollten Sie möglichst viel HTML Ihrer Website vom Server senden und die DOM-Größen für HTML, das auf dem Client gerendert werden muss, so klein wie möglich halten. Erwägen Sie alternative Architekturen, um die Bereitstellung von HTML an den Client zu beschleunigen und gleichzeitig das inkrementelle Parsing und Rendering zu nutzen, das der Browser für vom Server geladenes HTML bereitstellt.
Wenn Sie das clientseitige Rendering Ihrer Website so gering wie möglich halten können, verbessern Sie nicht nur den INP-Wert Ihrer Website, sondern auch andere Messwerte wie LCP, TBT und in einigen Fällen eventuell sogar Ihre TTFB.
Hero-Image von Unsplash von Maik Jonietz