Ich habe vor sehr langer Zeit, im Jahr 2008, als Yandex ein wenig suspendiert war und Yandex.Direct-Skripte, die synchron mit der Site verbunden waren, diese Site einfach getötet, die Code-Aufteilung kennengelernt. Im Allgemeinen war es damals normal, wenn Ihre „Skripte“ 10 Dateien sind, die Sie in der einzig richtigen Reihenfolge verbinden, was immer noch (mit Verzögerung) immer noch einwandfrei funktioniert.
Dann fing ich an, aktiv mit Karten zu arbeiten, und sie sind immer noch als externe Skripte verbunden, natürlich Lazy-Load. Dann habe ich als Mitglied des Yandex.Maps-Teams aktiv ymodules verwendet , um das Baumschütteln auf dem Client zu verwenden, was eine perfekte Codeaufteilung ermöglichte.
Und dann ging ich zu webpack
und React
, in das Land der verängstigten Idioten, die sich require.ensure
wie ein Widder an einem neuen Tor und tat es immer noch.
Das Aufteilen von Code ist kein Wow-Feature, sondern ein Muss. Trotzdem würde die SSR
nicht stören ...

Kleine Einführung
Heutzutage, wenn Bundles jeden Tag dicker werden, wird die Codeaufteilung wichtiger denn je. Zu Beginn kamen die Leute aus dieser Situation heraus, indem sie einfach separate Einstiegspunkte für jede Seite ihrer Anwendung erstellten, was im Allgemeinen gut ist, aber für SPA nicht funktioniert.
Dann kam die Funktion require.ensure
, heute bekannt als dynamic import
(nur Import), über die Sie einfach ein Modul anfordern können, das Sie etwas später erhalten.
Die erste Bibliothek über diesen Fall für React war reaktionsladbar , der Hype, um den es mir immer noch nicht sehr klar ist und der bereits gestorben ist (es hat einfach aufgehört, dem Autor zu gefallen).
Im Moment wird die mehr oder weniger "offizielle" Wahl React.lazy
und ladbare Komponenten (nur @loadable
) sein, und die Wahl zwischen ihnen ist offensichtlich:
- React.lazy kann SSR (Server Side Rendering) im Allgemeinen nicht verwenden. Selbst in Tests wird es ohne spezielle Tänze mit einem Tamburin fallen, wie zum Beispiel "synchrone Versprechen".
- Eine ladbare SSR kann und unterstützt zwar Suspense, ist aber nicht schlechter als React.Lazy.
Loadable unterstützt insbesondere schöne Wrapper zum Laden von Bibliotheken (loadable.lib, Sie können moment.js in React renderProp übernehmen) und hilft dem Webpack auf der Serverseite, eine Liste der verwendeten Skripte, Stile und Ressourcen für das Prefetching zu sammeln (das Webpack selbst nicht wirklich kennt). Lesen Sie im Allgemeinen die offizielle Dokumentation .
SSR
Im Allgemeinen liegt das ganze Problem in der SSR. Für CSR (Client Side Render) passt entweder React.lazy oder ein kleines Skript mit 10 Zeilen - dies wird definitiv ausreichen, und es macht keinen Sinn, eine große externe Bibliothek anzuschließen. Auf dem Server reicht dies jedoch nicht aus. Und wenn Sie keine SSR wirklich benötigen, können Sie das Lesen überspringen. Sie haben keine Probleme, die lange und schwer gelöst werden müssen.
SSR ist ein Schmerz. Ich bin (in gewisser Weise) einer der Betreuer von ladbaren Komponenten und es ist einfach schrecklich, wie viele Fehler an verschiedenen Orten auftreten. Und mit jedem Update fliegt das Webpack noch mehr.
SSR + CSS
Eine noch größere Ursache für Probleme mit SSRs ist CSS.
Wenn Sie Styled-Komponenten haben - es tut nicht so weh -, werden sie mit einem transform-stream
der selbst dem endgültigen Code das hinzufügt, was benötigt wird. Die Hauptsache ist, dass es überall eine Version von SC geben muss, sonst funktioniert der Fokus nicht - eine Version von SC kann nichts anderes über sich selbst erzählen, und SC liebt es, sich zu vermehren (überprüfen Sie Ihr Bundle). Um ehrlich zu sein, fällt gerade wegen dieser Einschränkung der Fokus normalerweise aus .
C Emotion ist einfacher - ihr styled
Adapter spuckt nur <style>
vor der Komponente selbst aus und das Problem ist gelöst. Einfach, billig und fröhlich. Im Prinzip ist es sehr mobilfreundlich und optimiert die Erstansicht erheblich. Aber ein bisschen verdirbt die Sekunde. Und persönlich erlaubt mir mein Gewissen nicht, solche Stile zu integrieren.
Mit normalem CSS (einschließlich des aus verschiedenen CSS-in-JS-Bibliotheken mit unterschiedlicher Magie erhaltenen) ist es noch einfacher - Informationen dazu finden Sie in der Webpack-Spalte, und es ist „bekannt“, welches CSS verbunden werden muss.
Verbindungsreihenfolge
Hier hat sich der Hund begraben. Wann soll ich mich verbinden?
Die SSR-freundliche ReactDOM.hydrate
dass Sie vor dem Aufrufen von ReactDOM.hydrate
alle "Komponenten" herunterladen müssen, die bereits in der Antwort des Servers vorhanden sind, die derzeit auf dem Client geladenen Skripts sich jedoch nicht leisten können.
Daher bieten alle Bibliotheken einen bestimmten Rückruf an, der aufgerufen wird, wenn alles geladen werden muss, und Sie können das Gehirn starten. Dies ist die Bedeutung der Arbeit von SSR-Codesplitting-Bibliotheken.
JS kann jederzeit geladen werden, und normalerweise wird ihre Liste am Ende des HTML-Codes hinzugefügt, aber CSS, damit keine FOUC vorhanden ist, muss am Anfang hinzugefügt werden.
Alle Bibliotheken können dies für den alten renderToString
, und nicht alle Bibliotheken können dies für renderToNodeStream
tun.
Es spielt keine Rolle, ob Sie nur JS (dies geschieht nicht) oder SC / Emotion (die sich selbst hinzufügen) haben. Aber - wenn Sie "nur CSS" haben - das ist es. Entweder sind sie am Ende, oder Sie müssen renderToString
oder eine andere Pufferung verwenden, die eine TTFB-Verzögerung (Time To First Byte) bietet und das Gefühl, diese SSR im Allgemeinen zu haben, geringfügig verringert.
Und natürlich - all dies ist an das Webpack gebunden und auf keine andere Weise. Daher schlage ich bei allem Respekt vor Greg, dem Autor von ladbaren Komponenten, vor, andere Optionen in Betracht zu ziehen.
Als nächstes folgt eine dreiteilige Agenda, deren Hauptidee darin besteht, etwas zu tun, das nicht getötet wird und nicht vom Bündler abhängig ist.
1. Reagierte importierte Komponente
React-Imported-Component ist kein schlechter "Loader" mit einer mehr oder weniger standardmäßigen Schnittstelle, die ladbaren Komponenten sehr ähnlich ist und SSR für alles kann, was sich bewegt.
Die Idee ist sehr einfach.
Sie müssen stats.json
, sich nicht an die Webpack-Optimierung anpassen (Verkettung oder allgemeiner Code) - Sie müssen nur die "Bezeichnung" eines Imports im Schlüssel im Array abgleichen und erneut importieren. Wie es als Teil eines bestimmten Bundlers ausgeführt wird, wie viele Dateien tatsächlich heruntergeladen werden und von wo ist nicht sein Problem.
Minus - Der Beginn des Ladens "verwendeter" Chunks erfolgt nach dem Laden des Hauptpakets, in dem die Zuordnung gespeichert ist. Dies ist etwas "später" als bei ladbaren Komponenten, bei denen diese Informationen direkt zu HTML hinzugefügt werden.
Ja, mit CCS funktioniert es in keiner Weise nach dem Wort.
2. gebrauchte Stile
Verwendete Stile funktionieren jedoch nur mit CSS, jedoch ähnlich wie reaktionsimportierte Komponenten.
- scannt alle CSS (im Build-Verzeichnis)
- merkt sich, wo welche Klasse definiert ist
- scannt die Ausgabe von renderToNodeStream (oder
renderToString
Antwort von renderToString
) - Findet class = 'XXX', stimmt mit der Datei überein und spuckt sie in der Serverantwort aus.
- (Nun, und teleportiert dann alle diese Stile zum Kopf, um das Hydrat nicht zu brechen). Stilkomponenten funktionieren genauso.
Es gibt keine TTBT-Verzögerung, sie ist nicht an den Bündler gebunden - ein Märchen. Funktioniert wie eine Uhr, wenn die Stile gut geschrieben sind.
React-Import-Komponente + gebrauchte Stile + Paketarbeitsbeispiel .
Nicht der offensichtlichste Bonus - auf dem Server erledigen beide Bibliotheken während des Startvorgangs "alles, was benötigt wird", bis der Express-Server den ersten Client empfangen kann, und werden sowohl auf dem Server als auch während der Tests vollständig synchronisiert.
3. Reader-Prerendered-Komponente
Und die Bibliothek schließt die ersten drei, was "teilweise Rehydration" bewirkt, und zwar auf eine so großväterliche Weise, dass ich mich sofort wundere. Sie fügt wirklich "Diven" hinzu.
- auf dem Server:
- wickelt ein Stück Holz in eine Div mit einem "berühmten Ausweis"
- auf dem Client:
- Der Komponentenkonstruktor findet sein eigenes div
- kopiert sein innerHTML, bevor React es nimmt.
- Verwendet diesen HTML-Code, bis der Client bereit ist, ihn zu
hydrate
- Technisch ermöglicht dies die Verwendung von Hybrid SSR (Rendertron)
const AsyncLoadedComponent = loadable(() => import('./deferredComponent')); const AsyncLoadedComponent = imported(() => import('./deferredComponent')); <PrerenderedComponent live={AsyncLoadedComponent.preload()} // when Promise got resolve - component will go "live" > <AsyncLoadedComponent /> // meanwhile you will see "preexisting" content </PrerenderedComponent>
Dieser Fokus funktioniert nicht mit ladbaren Komponenten, da er nicht von einem Versprechen zum Vorspannen zurückkehrt . Dies ist besonders wichtig für Bibliotheken wie React -Snap (und andere "Prerender"), die "Inhalt" haben, aber keine "echte" SSR durchlaufen haben.

Aus Sicht des Codes sind dies 10 Zeilen plus etwas mehr, um stabile SSR-CSR-UIDs zu erhalten, die die zufällige Reihenfolge des Ladens und der Ausführung des Codes berücksichtigen.
Boni:
- Sie müssen nicht auf das "Laden aller Skripte" warten, bevor Sie die Gehirne starten - die Gehirne werden gestartet, sobald sie bereit sind
- Sie müssen überhaupt keine Gehirne laden, sodass SSR-Daten übrig bleiben (wenn keine SSR-Version vorhanden ist, werden die Gehirne weiterhin geladen). Genau wie in jQuery-Zeiten.
- Sie können auch das Stream- Caching großer Renderblöcke implementieren (theoretisch Suspence-kompatibel) - wiederum mithilfe des Transformations-Streams.
- und Serialisieren / Deserialisieren des Status in / aus HTML, wie während jQuery
Im Prinzip waren Serialisierung und Deserialisierung die Hauptidee bei der Erstellung einer Bibliothek, um das Problem der Duplizierung des Zustands zu lösen (Bild aus dem Artikel über SSR). Caching kam später an.

Insgesamt
Insgesamt gibt es drei Ansätze, mit denen Sie Ihre Sicht auf SSR und Codeaufteilung ändern können. Der erste funktioniert mit JS-Codesplitting und bricht nicht. Der zweite funktioniert mit CSS-Codesplitting und bricht nicht. Die dritte Methode vereinfacht und beschleunigt einige Prozesse auf HTML-Ebene und bricht auch hier nicht ab.
Links zu Bibliotheken:
Artikel (in englischer Sprache)