Das Rendern von HTML mit JavaScript unterscheidet sich vom Rendern von HTML, das vom Server gesendet wird. Dies kann die Leistung beeinträchtigen. In diesem Leitfaden erfährst du mehr über die Unterschiede und darüber, wie du die Rendering-Leistung deiner Website aufrechterhalten kannst – insbesondere, wenn es um Interaktionen geht.
Das Parsen und Rendern von HTML ist etwas, das Browser standardmäßig sehr gut für Websites leisten, die die integrierte Navigationslogik des Browsers verwenden – manchmal auch als "herkömmlicher Seitenaufbau" bezeichnet oder schwere Navigationen. Solche Websites werden auch als Multi-Page-Anwendungen (MPAs) bezeichnet.
Entwickler können jedoch Browser-Standardeinstellungen umgehen, um sie an ihre Anwendungsanforderungen anzupassen. Dies gilt sicherlich für Websites, die das SPA-Muster (Single Page Application) verwenden, bei dem mit JavaScript große Teile des HTML/DOM auf dem Client dynamisch erstellt werden. Dieses Designmuster wird als clientseitiges Rendering bezeichnet. Es kann sich auf die Interaction to Next Paint (INP) Ihrer Website auswirken, wenn der Aufwand übermäßig hoch ist.
Dieser Leitfaden wird Ihnen helfen, den Unterschied zwischen der Verwendung von HTML, das vom Server an den Browser gesendet wird, und dem Erstellen auf dem Client mit JavaScript abzuwägen, und wie Letzteres in entscheidenden Momenten zu einer hohen Interaktionslatenz führen kann.
Wie der Browser vom Server bereitgestellte HTML-Inhalte rendert
Bei dem beim herkömmlichen Laden von Seiten verwendeten Navigationsmuster muss bei jeder Navigation HTML vom Server empfangen werden. 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 Teilen.
Der letzte Schritt ist entscheidend. Außerdem ist es eine der grundlegendsten Leistungsoptimierungen beim Server-/Browser-Austausch und wird als Streaming bezeichnet. Wenn der Server so schnell wie möglich mit dem Senden von HTML beginnen kann und der Browser nicht auf das Eintreffen der gesamten Antwort wartet, kann der Browser HTML beim Eintreffen stückweise verarbeiten.
<ph type="x-smartling-placeholder">Wie die meisten Browseraktivitäten findet das Parsen von HTML innerhalb von Aufgaben statt. Wenn HTML vom Server zum Browser gestreamt wird, optimiert der Browser das Parsen dieses HTML-Codes jeweils Schritt für Schritt, wenn Teile dieses Streams in Blöcken ankommen. Dies hat zur Folge, dass der Browser nach der Verarbeitung jedes Blocks regelmäßig dem Hauptthread nachgibt. Dadurch werden lange Aufgaben vermieden. Das bedeutet, dass während des Parsens von HTML andere Arbeiten stattfinden können, einschließlich des inkrementellen Rendering-Aufwands, der erforderlich ist, um dem Nutzer eine Seite zu präsentieren, sowie die Verarbeitung von Nutzerinteraktionen, die während der entscheidenden Startphase der Seite stattfinden können. Dieser Ansatz führt zu einem besseren Interaction to Next Paint (INP)-Wert für die Seite.
Das bedeutet: Wenn Sie HTML vom Server streamen, wird HTML inkrementell geparst und gerendert, und Sie erhalten die kostenlose Bereitstellung an den Hauptthread. Beim clientseitigen Rendering ist das nicht der Fall.
Wie der Browser HTML-Code rendert, der von JavaScript bereitgestellt wird
Für jede Navigationsanforderung zu einer Seite muss der Server etwas HTML-Code bereitstellen, einige Websites verwenden jedoch das SPA-Muster. Bei diesem Ansatz wird häufig eine minimale anfängliche HTML-Nutzlast vom Server bereitgestellt. Anschließend füllt der Client jedoch den Hauptinhaltsbereich einer Seite mit HTML, das aus Daten zusammengesetzt ist, die vom Server abgerufen wurden. Nachfolgende Navigationen, manchmal auch als „weiche Navigationen“ bezeichnet in diesem Fall vollständig von JavaScript verarbeitet werden, um die Seite mit neuem HTML-Code zu füllen.
In weniger Fällen, in denen HTML über JavaScript dem DOM dynamisch hinzugefügt wird, kann auch bei Nicht-SPAs das clientseitige Rendering auftreten.
Es gibt einige gängige Möglichkeiten, HTML-Code zu erstellen oder über JavaScript zum DOM hinzuzufügen:
- Mit der Property
innerHTML
können Sie den Inhalt eines vorhandenen Elements über einen String festlegen, den der Browser in DOM parst. - Mit der Methode
document.createElement
können Sie neue Elemente erstellen, die dem DOM hinzugefügt werden sollen, ohne dass HTML-Parsing im Browser verwendet werden muss. - Mit der
document.write
-Methode können Sie HTML-Code in das Dokument schreiben (und der Browser parst es wie in Ansatz 1). Aus verschiedenen Gründen wird von der Nutzung vondocument.write
jedoch abgeraten.
Die Erstellung von HTML/DOM über clientseitiges JavaScript kann erhebliche Auswirkungen haben:
- Im Gegensatz zu HTML, das als Antwort auf eine Navigationsanforderung vom Server gestreamt wird, werden JavaScript-Aufgaben auf dem Client nicht automatisch aufgeteilt. Dies kann zu langen Aufgaben führen, die den Hauptthread blockieren. Dies bedeutet, dass der INP-Wert Ihrer Seite negativ beeinflusst werden kann, wenn Sie zu viel HTML/DOM auf dem Client gleichzeitig erstellen.
- Wenn beim Start auf dem Client HTML 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 jedoch nicht, dass der LCP Ihrer Website durch die Umgehung dieser grundlegenden Optimierung der Browserleistung beeinträchtigt wird.
Auswirkungen des clientseitigen Renderings auf die Leistung
Wenn Ihre Website stark vom clientseitigen Rendering abhängig ist und Sie schlechte INP-Werte in Ihren Felddaten beobachten, fragen Sie sich vielleicht, ob das clientseitige Rendering etwas mit dem Problem zu tun hat. Wenn es sich bei Ihrer Website beispielsweise um eine SPA handelt, können Ihre Felddaten Interaktionen aufzeigen, die für einen erheblichen Renderingaufwand verantwortlich sind.
Was auch immer die Ursache ist, hier sind einige mögliche Ursachen, die Sie untersuchen können, um die Situation wieder in Schwung zu bringen.
Stellen Sie so viel HTML-Code wie möglich vom Server bereit.
Wie bereits erwähnt, verarbeitet der Browser standardmäßig den HTML-Code des Servers sehr leistungsfähig. Dadurch wird das Parsen und Rendern von HTML so aufgeteilt, dass lange Aufgaben vermieden werden, und die Gesamtzeit des Hauptthreads wird optimiert. Dies führt zu einer kürzeren Total Blocking Time (TBT) und TBT korreliert stark mit INP.
Sie verlassen sich beim Erstellen Ihrer Website möglicherweise auf ein Frontend-Framework. In diesem Fall sollten Sie sicherstellen, dass Sie den HTML-Code der Komponente auf dem Server rendern. Dadurch wird die Menge des anfänglichen clientseitigen Renderings Ihrer Website eingeschränkt, was zu einer besseren Nutzererfahrung führen sollte.
- Für React kannst du die Server DOM API verwenden, 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 Time to First Byte (TTFB) sowie zu nachfolgenden Messwerten wie First Contentful Paint (FCP) und LCP führen kann. Achten Sie darauf, dass Sie nach Möglichkeit die Streaming APIs für Node.js oder andere JavaScript-Laufzeiten verwenden, damit der Server so schnell wie möglich mit dem Streaming von HTML an den Browser beginnen kann. Next.js ist ein React-basiertes Framework und bietet standardmäßig viele Best Practices. Der Browser kann nicht nur automatisch HTML auf dem Server rendern, sondern auch statisch HTML für Seiten generieren, die sich je nach Nutzerkontext (z. B. Authentifizierung) nicht ändern.
- Vue führt außerdem standardmäßig clientseitiges Rendering aus. Vue kann jedoch genau wie React den HTML-Code Ihrer Komponente auf dem Server rendern. Nutzen Sie nach Möglichkeit diese serverseitigen APIs oder erwägen Sie eine höhere Abstraktion für Ihr Vue-Projekt, um die Implementierung der Best Practices zu vereinfachen.
- Svelte rendert standardmäßig HTML auf dem Server. Wenn Ihr Komponentencode jedoch Zugriff auf browserexklusive Namespaces (z. B.
window
) benötigt, können Sie den HTML-Code dieser Komponente möglicherweise nicht auf dem Server rendern. Informiere dich nach Möglichkeit über alternative Ansätze, damit du kein unnötiges clientseitiges Rendering verschwendest. SvelteKit – für Svelte im Sinne von Next.js für React – bettet möglichst viele Best Practices in Ihre Svelte-Projekte ein, damit Sie potenzielle Fallstricke bei Projekten vermeiden können, die nur Svelte verwenden.
Anzahl der auf dem Client erstellten DOM-Knoten begrenzen
Wenn DOMs groß sind, nimmt die Verarbeitung, die zum Rendern erforderlich ist, 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 einfügt, sollten Sie diese DOMs so klein wie möglich halten. Auf diese Weise wird der Aufwand beim clientseitigen Rendering zur Anzeige dieses HTML-Codes reduziert und der INP-Wert Ihrer Website hoffentlich gesenkt.
Architektur eines Streaming-Service-Workers in Betracht ziehen
Dies ist eine fortgeschrittene Technik, die möglicherweise nicht für jeden Anwendungsfall einfach funktioniert. Mit ihr können Sie jedoch aus Ihrer MPA-Datei eine Website machen, die sich so anfühlt, als würde sie sofort geladen werden, wenn Nutzende von einer Seite zur nächsten navigieren. Sie können einen Service Worker verwenden, um die statischen Teile Ihrer Website in CacheStorage
vorab im Cache zu speichern, während Sie mit der ReadableStream
API den Rest des HTML-Codes einer Seite vom Server abrufen.
Wenn Sie diese Technik erfolgreich anwenden, 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, die diesen Ansatz verwenden, können sich fast wie eine SPA anfühlen, jedoch ohne die Nachteile des clientseitigen Renderings. Außerdem wird dadurch die Menge des vom Server angeforderten HTML-Codes reduziert.
Kurz gesagt: Eine Streaming Service Worker-Architektur ersetzt nicht die integrierte Navigationslogik des Browsers, sondern ersetzt sie. Weitere Informationen dazu, wie Sie dies mit Workbox erreichen, finden Sie unter Schnellere mehrseitige Anwendungen mit Streams.
Fazit
Die Art und Weise, wie Ihre Website HTML empfängt und rendert, hat Auswirkungen auf die Leistung. Wenn Sie sich darauf verlassen, dass der Server den gesamten (oder einen Großteil) für die Funktion Ihrer Website erforderlichen HTML-Code sendet, erhalten Sie eine Menge kostenlos: inkrementelles Parsen und Rendering sowie automatische Nachgabe an den Hauptthread, um lange 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 Anforderungen der einzelnen Websites lässt sich dies jedoch nicht in 100% der Fälle vermeiden. Um die potenziell langen Aufgaben, die durch übermäßiges Rendering auf der Client-Website entstehen können, abzumildern, sollten Sie nach Möglichkeit möglichst viel vom HTML-Code Ihrer Website vom Server senden, die DOM-Größen für HTML-Code, der auf dem Client gerendert werden muss, so klein wie möglich halten und alternative Architekturen in Betracht ziehen, um die Bereitstellung von HTML an den Client zu beschleunigen und gleichzeitig das inkrementelle Parsen und Rendering des Browsers für HTML-Code, der vom Server geladen wird, zu nutzen.
Wenn Sie das clientseitige Rendering Ihrer Website so gering wie möglich halten können, verbessern Sie nicht nur den INP Ihrer Website, sondern auch andere Messwerte wie LCP, TBT und in einigen Fällen sogar Ihre TTFB.
Hero-Image von Unsplash von Maik Jonietz