Server Side Rendering (SSR) ist eine sehr nützliche Technik, die Web-Apps schneller erscheinen lässt. Die anfängliche HTML wird angezeigt, bevor das JavaScript geparst wird, und während der Benutzer entscheidet, worauf er tippen soll, sind unsere Handler bereit.
Server-Side-Rendering in React erfordert zusätzliche Arbeit bei der Einrichtung und verursacht Serverkosten. Darüber hinaus, wenn Ihr Serverteam keine JavaScript-Ausführung auf Ihren Servern durchführen kann, sind Sie gefangen. Es kompliziert die Einrichtung des CDN erheblich, insbesondere wenn Sie Seiten haben, die ein Login erfordern und bei denen die Benutzerinformationen verwaltet werden.
Ich möchte ein neues Konzept namens Render Caching vorstellen. Dies ist ein cooler Trick, der Benutzern eine sofortige Leistungssteigerung wie bei SSR bieten kann, ohne dass Code auf dem Server geschrieben werden muss.
Was ist Render Caching?
Die Migration von statischen HTML-Seiten zu Single Page Apps (SPAs) hat eine klaffende Lücke im gesamten Konzept des Cachings hinterlassen, auf das sich das Web traditionell verlassen hat. Während Browser die Auslieferung und das Rendern des anfänglichen HTML optimieren, lässt eine SPA diese leer, um später gefüllt zu werden.
Render Caching optimiert das SPA-Rendering und kann die wahrgenommene Ladezeit von Webseiten erheblich verbessern. Dies geschieht durch das Caching des gerenderten HTML im Browser für den nächsten Ladevorgang und kann diese Anzeige ohne das Parsen von JavaScript bereitstellen, das unsere Anzeigezeit beansprucht.
Render Caching aktivieren
Wir haben bereits erwähnt, dass die Einrichtung von SSR für React zusätzlichen Aufwand und Serverkosten verursacht. Render Caching vermeidet diese Belastungen.
Die Einrichtung erfordert ein paar Schritte. Lassen Sie uns das in verdauliche Stücke aufteilen.
Schritt 1: Ermitteln des korrekten Caching-Zustands
Finden Sie die Bedingungen für die aktuelle Seite heraus, bei denen sie bei einem erneuten Besuch des Benutzers gleich gerendert wird.
Sie könnten zum Beispiel ein JSON-Objekt mit der aktuellen Build-Nummer oder einer Benutzer-ID erstellen. Entscheidend ist, dass der Zustand in der URL, im lokalen Speicher oder in Cookies gekapselt ist und kein Serveraufruf dafür benötigt wird.
Schritt 2: API-Aufrufe einrichten
Stellen Sie sicher, dass alle API-Aufrufe vor dem Renderaufruf an React erfolgen. Das ist auch in regulären Anwendungsfällen sinnvoll, wo wir verhindern wollen, dass sich die Seite unter dem Benutzer ändert, was zu Flackern führt.
Schritt 3: Lokal im Unload-Handler cachen
Fügen Sie nun einen Unload-Event-Handler zum Dokument hinzu. Speichern Sie das aktuelle DOM in localStorage/indexDB.
Das sieht dann etwa so aus, wobei eine Build-Nummer und eine Benutzer-ID verwendet werden, um den in Schritt 1 behandelten Caching-Zustand zu bestimmen.
window.addEventListener("beforeunload", () => {
// Production code would also be considerate of localStorage size limitations
// and would do a LRU cache eviction to maintain sanity on storage.
// There should also be a way to clear this data when the user signs out
window.localStorage.setItem(
`lastKnown_${window.location.href}`,
JSON.stringify({
conditions: {
userId: "<User ID>",
buildNo: "<Build No.>"
},
data: document.getElementById("content").innerHTML
})
);
});
// If you want to store data per user, you can add user ID to the key instead of the condition.
Schritt 4: Den letzten bekannten Zustand beim Laden wiederherstellen
Als Nächstes wollen wir den letzten bekannten Zustand aus dem lokalen Speicher des Browsers abrufen, um ihn bei zukünftigen Besuchen verwenden zu können. Dies tun wir, indem wir Folgendes zur HTML-Datei hinzufügen (z. B. `index.html` unter dem `body`-Tag des Dokuments).
<!-- ... -->
</body>
<script>
let lastKnownState = window.localStorage.getItem(`lastKnown_${window.location.href}`);
lastKnownState = lastKnownState && JSON.parse(lastKnownState);
if (lastKnownState &&
lastKnownState.conditions.userId === "<User ID>" &&
lastKnownState.conditions.buildNo === "<Build No.>") {
document.getElementById('content').innerHTML = lastKnownState.data;
window.hasRestoredState = true;
}
</script>
Schritt 5: Den letzten bekannten Zustand in React rendern
Hier wird es ernst. Nachdem wir nun den letzten bekannten Zustand des Benutzers im DOM sichtbar gemacht haben, können wir den vollständigen Inhalt abrufen und unsere App in diesem Zustand rendern, indem wir die oberste Ebene von Reacts Rendering mit hydrate bedingt aktualisieren. Event-Handler werden funktionsfähig, sobald dieser Code ausgeführt wird, aber das DOM sollte sich nicht ändern.
import {render, hydrate} from "react-dom"
if (window.hasRestoredState) {
hydrate(<MyPage />, document.getElementById('content'));
} else {
render(<MyPage />, document.getElementById('content'));
}
Schritt 6: Vollständig asynchron machen
Ändern Sie Ihre Skript-Tags von sync auf async/defer, um die JavaScript-Dateien zu laden. Dies ist ein weiterer wichtiger Schritt, um ein reibungsloses Lade- und Rendering-Erlebnis auf der Benutzeroberfläche zu gewährleisten.
Das ist alles! Laden Sie die Seite neu, um den Leistungsschub zu sehen.
Leistungsverbesserung messen
Okay, Sie haben all diese Arbeit geleistet und möchten nun wissen, wie performant Ihre Website ist. Sie werden die Verbesserungen benchmarken wollen.
Render Caching glänzt in Situationen, in denen Sie mehrere Serveraufrufe haben, bevor Sie wissen, was gerendert werden soll. Auf Skript-lastigen Seiten kann JavaScript tatsächlich viel Zeit zum Parsen beanspruchen.
Sie können die Ladeleistung im Tab "Performance" in den Chrome DevTools messen.

Idealerweise würden Sie ein Gastprofil verwenden, damit Ihre Browser-Erweiterungen die Messungen nicht stören. Sie sollten beim Neuladen eine deutliche Verbesserung feststellen. Im obigen Screenshot sehen wir eine Beispiel-App mit einem asynchronen `data.json`-Fetch-Aufruf, der vor dem Aufruf von `ReactDOM.hydrate` ausgeführt wird. Mit Render Caching ist das Rendering abgeschlossen, noch bevor die Daten geladen sind!
Zusammenfassung
Render Caching ist eine clevere Technik, um sicherzustellen, dass die wahrgenommene Geschwindigkeit von erneuten Abrufen derselben Webseite schneller ist, indem eine Caching-Schicht für das finale HTML hinzugefügt und diese dem Benutzer angezeigt wird. Benutzer, die Ihre Website häufig besuchen, profitieren am meisten.
Wie Sie sehen können, haben wir dies mit sehr wenig Code erreicht und die daraus resultierenden Leistungssteigerungen sind enorm. Probieren Sie es auf Ihrer Website aus und hinterlassen Sie Ihre Kommentare. Ich würde gerne erfahren, ob Ihre Website-Leistung die gleichen deutlichen Verbesserungen erzielt, die ich erfahren habe.
„Stellen Sie sicher, dass alle API-Aufrufe vor dem Renderaufruf an React erfolgen.“
Das ist eine ziemlich knifflige Aufgabe, da es bedeutet, die übliche React-Praxis des Aufrufs von API-Aufrufen pro Komponente in `componentDidMount` nicht zu befolgen. Haben Sie ein Beispiel dafür, wie Sie es machen würden?
Hallo Anon,
Wenn Sie bis zu `componentDidMount` warten, um Serveraufrufe durchzuführen, können die Serveraufrufe erst erfolgen, nachdem die Komponente einen Render-Durchlauf durchlaufen hat, was zu mindestens einem Flackern führt, selbst wenn kein Render-Caching vorhanden ist. Bei Render-Caching löscht der erste Render-Durchlauf die gerenderten Daten aus dem DOM. Wenn dieselben Daten von zwei Komponenten benötigt werden, sind zwei API-Aufrufe erforderlich.
Ich halte meine Rendering- und Datenkomponenten getrennt und verwende eine State-Management-Bibliothek, um die Informationen zwischen den beiden zu übergeben. Daher kann der Serverzugriff unabhängig vom Rendering und näher am Routing erfolgen. Derselbe Ansatz wird aus denselben Gründen für das Server-Side-Rendering verwendet.
Serveraufrufe in `componentDidMount` durchzuführen, ist der empfohlene Weg, da `componentWillMount` vor dem Mounten mehr als einmal aufgerufen werden kann, und wenn Sie es im Konstruktor oder in `componentWillMount` tun, kann der Aufruf zurückkehren, bevor die Komponente gerendert wird, und es ist illegal, `setState` aufzurufen, bevor die Komponente gerendert wurde.
Ich argumentiere nicht für `componentWillMount`. Der Vorschlag ist, den Serverzugriff aus React zu verlagern und die Daten in einer State-Management-Bibliothek wie MobX oder Redux zu speichern, anstatt den React-State zu verwenden, wenn Sie Render-Caching benötigen. Dann können Sie die Daten einrichten, bevor React gemountet wird.
In Demystifying server-side rendering in React stellt Alex Moldovan ein Muster vor, das eine statische Deklaration namens `serverFetch` in Kombination mit Redux Thunks nutzt. Ich habe diese Idee sowie viele andere aufgegriffen und in Paragons integriert, das SSR Out-of-the-Box anbietet.
David Munger berichtet, dass der folgende Fehler in React 17 auftritt: „Warnung: Unerwartete Server-HTML, die ein
<div>in<div>enthält.“