Heute veröffentlichen wir eine Übersetzung einer Geschichte darüber, wie der Übergang von
React Boilerplate zu
Next.js , einem Framework für die Entwicklung fortschrittlicher Webanwendungen auf der Basis von React, das Laden der
Homepage des Manifold.co-Projekts um das 7,5-fache beschleunigt hat. Es wurden keine weiteren Änderungen am Projekt vorgenommen, und dieser Übergang erwies sich im Allgemeinen als für andere Teile des Systems völlig unsichtbar. Was sich am Ende herausstellte, erwies sich als noch besser als erwartet.

Ergebnisübersicht
Tatsächlich können wir sagen, dass der Übergang zu Next.js uns so etwas wie eine „Steigerung der Projektproduktivität, die aus dem Nichts kam“ brachte. So sieht die Ladezeit des Projekts aus, wenn verschiedene Hardwareressourcen und Netzwerkverbindungen verwendet werden.
Verbindung
| CPU
| Zu Sekunden
| Nach Sekunden
| % Verbesserung
|
Schnell (200 Mbit / s)
| Schnell
| 1.5
| 0,2
| 750
|
Medium (3G)
| Schnell
| 5.6
| 1.1
| 500
|
Medium (3G)
| Mittel
| 7.5
| 1.3
| 570
|
Langsam (langsame 3G-Verbindung)
| Mittel
| 22
| 4
| 550
|
Bei Verwendung einer Schnellverbindung und eines Geräts mit einem schnellen Prozessor verringerte sich die Ladezeit des Standorts von 1,5 s. bis zu 0,2 s, dh dieser Indikator verbesserte sich um das 7,5-fache. Bei einer Verbindung mittlerer Qualität und einem Gerät mit durchschnittlicher Leistung verringerte sich die Ladezeit des Standorts von 7,5 s. bis zu 1,3 s
Was passiert, wenn ein Benutzer auf eine URL klickt?
Um die Funktionen der Arbeit von progressiven Webanwendungen (Progressive Web App, PWA) zu verstehen, müssen Sie zunächst herausfinden, was zwischen dem Zeitpunkt, zu dem der Benutzer die URL (an der Adresse unserer Website) aufruft, und dem Zeitpunkt, zu dem er etwas sieht, geschieht in einem Browserfenster (in diesem Fall unsere React-Anwendung).
AnwendungsstufenBetrachten Sie die 5 Phasen der Arbeit mit der Anwendung, deren Diagramm oben angegeben ist.
- Der Benutzer geht zur URL, das System ermittelt die Serveradresse über DNS und greift auf den Server zu. All dies ist extrem schnell erledigt, normalerweise dauert es weniger als 100 Millisekunden, aber dieser Schritt dauert einige Zeit, weshalb er hier erwähnt wird.
- Jetzt gibt der Server den HTML-Code der Seite zurück, aber die Seite im Browser bleibt leer, bis die für die Anzeige erforderlichen Ressourcen geladen sind (es sei denn, die Ressourcen werden asynchron geladen). Tatsächlich finden zu diesem Zeitpunkt mehr Aktionen statt als in der Abbildung dargestellt, aber eine gemeinsame Überprüfung all dieser Prozesse wird uns auch gefallen.
- Nach dem Laden des HTML-Codes und der wichtigsten Ressourcen zeigt der Browser an, was angezeigt werden kann, und lädt weiterhin alles andere (z. B. Bilder) im Hintergrund. Haben Sie sich jemals gefragt, warum Bilder manchmal plötzlich schneller als nötig auf einer Seite „auftauchen“ und manchmal zu lange geladen werden? Genau deshalb passiert dies. Mit diesem Ansatz können Sie schnell eine fertige Seite erstellen.
- JavaScript-Code kann erst nach dem Laden analysiert und ausgeführt werden. Abhängig von der Größe des auf der Seite verwendeten JS-Codes (und dies kann für eine typische React-Anwendung sehr groß sein, wenn der Code in einer einzelnen Datei gepackt ist) kann dies einige Sekunden oder sogar länger dauern (beachten Sie, dass JS Der Code muss nicht auf das Laden aller anderen Ressourcen warten, um mit der Ausführung zu beginnen, obwohl er im Diagramm genau so aussieht.
- Im Fall einer React-Anwendung kommt jetzt der Moment, in dem der Code das DOM ändert, wodurch der Browser die bereits angezeigte Seite neu zeichnet. Dann beginnt ein weiterer Zyklus zum Laden von Ressourcen. Die Zeit, die dieser Schritt benötigt, hängt von der Komplexität der Seite ab.
Je schneller, desto besser.
Da eine progressive Webanwendung React-Code verwendet und statischen HTML- und CSS-Code erzeugt, bedeutet dies, dass der Benutzer die React-Anwendung bereits in Schritt 3 des obigen Schemas und nicht in Schritt 5 sieht. In unseren Tests dauert dies 0,2 bis 4 Sekunden Dies hängt von der Geschwindigkeit der Verbindung des Benutzers mit dem Internet und seinem Gerät ab. Dies ist viel besser als die vorherigen 1,5-22 Sekunden. Progressive Webanwendungen sind eine zuverlässige Möglichkeit, React-Anwendungen schneller an den Benutzer zu liefern.
Der Grund, warum progressive Webanwendungen und verwandte Frameworks wie Next.js immer noch nicht sehr beliebt sind, liegt darin, dass JS-Frameworks traditionell keinen besonders erfolgreichen statischen HTML-Code generieren. Heute hat sich alles sehr verändert, da Frameworks wie React, Vue und Angular und andere das serverseitige Rendern hervorragend unterstützen. Um diese Tools verwenden zu können, benötigen Sie jedoch noch ein tiefes Verständnis der Funktionen der Arbeit von Bundlern und Projektassemblierungswerkzeugen. Die Arbeit mit all dem ist nicht ohne Probleme.
Das jüngste Aufkommen von PWA-Frameworks wie Next.js und Gatsby (beide erschienen Ende 2016 - Anfang 2017) ist aufgrund der geringeren Eintrittsbarrieren und der Tatsache, dass die Verwendung solcher Frameworks eine einfache und unterhaltsame Aufgabe war, zu einem ernsthaften Schritt in Richtung einer breiten Akzeptanz von PWA geworden.
Obwohl nicht jede Anwendung auf Next.js übertragen werden kann, bedeutet dieser Übergang für viele React-Anwendungen dieselbe „Leistung aus dem Nichts“, von der wir hier sprechen, ergänzt durch eine noch effizientere Nutzung der Netzwerkressourcen.
Wie schwierig ist die Migration zu Next.js?
Im Allgemeinen ist festzustellen, dass die Übersetzung unserer Homepage in Next.js nicht sehr schwierig war. Wir sind jedoch auf einige Schwierigkeiten gestoßen, die durch die Architekturfunktionen unserer Anwendung verursacht wurden.
▍ Ablehnen eines React Routers
Wir mussten den React-Router aufgeben, da Next.js über einen eigenen integrierten Router verfügt, der besser mit Optimierungen hinsichtlich der Codetrennung kombiniert werden kann, die zusätzlich zur PWA-Architektur durchgeführt werden. Auf diese Weise kann dieser Router viel schneller Seiten laden, als Sie es von einem clientseitigen Router erwarten würden.
Der Next.js-Router ist ein bisschen wie ein Hochgeschwindigkeits-React-Router, aber immer noch kein React-Router.
In der Praxis bestand der Übergang zum Next.js-Router für uns darin, einfach die Standardkomponente des React-Routers durch die entsprechende Next.js-Komponente zu ersetzen, da wir die besonders erweiterten Funktionen des React-Routers nicht genutzt haben:
/* ( React) */ <Link to="/my/page"> A link </Link> /* ( Next.js) */ <Link href="/my/page" passHref> <a> A link </a> </Link>
Im Allgemeinen stellte sich heraus, dass alles nicht so schlecht war. Wir mussten die Eigenschaft umbenennen und ein Tag für Server-Rendering-Zwecke hinzufügen. Da wir auch die Bibliothek mit den
styled-components
verwendet haben, stellte sich heraus, dass wir in den meisten Fällen die
passHref
Eigenschaft hinzufügen
passHref
, um sicherzustellen, dass sich das System so verhält, dass
href
immer auf das generierte Tag verweist.
Netzwerkanfragen für vielfältig.coUm die Optimierung des Next.js-Routers in Aktion mit eigenen Augen zu sehen, öffnen Sie die Registerkarte Netzwerk der Browser-Entwicklertools, indem Sie die Seite manifestold.co anzeigen und auf einen Link klicken. Die vorherige Abbildung zeigt das Ergebnis des Klickens auf den Link
/services
. Wie Sie sehen, führt dies zur Ausführung einer Anforderung zum Laden von
services.js
anstatt eine reguläre Anforderung auszuführen.
Ich spreche nicht nur von clientseitigem Routing, der React-Router eignet sich auch zur Lösung dieses Problems. Ich spreche von einem echten Stück JavaScript-Code, der aus dem Rest des Codes extrahiert und auf Anfrage geladen wurde. Dies erfolgt mit dem Standard Next.js. Und das ist viel besser als das, was wir vorher hatten. Es handelt sich nämlich um ein großes Paket von JS-Code mit einer Größe von 1,7 MB, das der Client herunterladen und verarbeiten musste, bevor er etwas sehen konnte.
Obwohl die hier vorgestellte Lösung nicht perfekt ist, ist sie der Idee, dass Benutzer nur Code für die von ihnen angezeigten Seiten herunterladen, viel näher als die vorherige.
▍ Funktionen zur Verwendung von Redux
Wenn man das Thema der Schwierigkeiten fortsetzt, die mit dem Übergang zu Next.js verbunden sind, kann man feststellen, dass alle interessanten Optimierungen, die Next.js der Anwendung unterzieht, einen gewissen Einfluss auf diese Anwendung haben. Da Next.js eine Codetrennung auf Seitenebene durchführt, kann der Entwickler nicht auf die
React
Stammkomponente oder die
render()
-Methode der
React
react-dom
Bibliothek zugreifen. Wenn Sie bereits an der Konfiguration von Redux beteiligt waren, können Sie feststellen, dass dies alles darauf hinweist, dass wir für den normalen Betrieb mit Redux das Problem lösen müssen, da nicht klar ist, wo genau nach Redux gesucht werden soll.
Next.js bietet mit Redux eine spezielle Komponente höherer Ordnung, die als Wrapper für alle Komponenten der obersten Ebene auf jeder Seite fungiert:
export default withRedux(HomePage);
Dies alles ist zwar nicht so schlimm, aber wenn Sie
createStore()
-Methoden benötigen, z. B. bei Verwendung von
Redux-Reducer-Injektoren , müssen Sie zusätzliche Zeit zum Debuggen des Wrappers benötigen (und versuchen Sie es übrigens nie Verwenden Sie so etwas wie
redux-reducer-injectors
.
Aufgrund der Tatsache, dass Redux jetzt eine „Black Box“ ist, wird die Verwendung der
unveränderlichen Bibliothek damit problematisch. Obwohl die Tatsache, dass Immutable mit Redux funktioniert, ziemlich offensichtlich ist, bin ich auf ein Problem gestoßen. Entweder war der Status der obersten Ebene nicht unveränderlich (
get is not a function
), oder die Wrapper-Komponente hat versucht, die Punktnotation für die Arbeit mit JS-Objekten anstelle der Methode
.get()
verwenden (
Can't get catalog of undefined
Fehler
Can't get catalog of undefined
.get()
werden). Um dieses Problem zu beheben, musste ich auf den Quellcode verweisen. Schließlich zwingt Next.js den Entwickler aus einem bestimmten Grund, seine eigenen Mechanismen zu verwenden.
Im Allgemeinen kann festgestellt werden, dass das Hauptproblem bei Next.js darin besteht, dass nur sehr wenig in diesem Framework gut dokumentiert ist. Es gibt viele Beispiele in der
Dokumentation, auf deren Grundlage Sie etwas Eigenes erstellen können. Wenn es jedoch keines gibt, das die Merkmale Ihres Projekts widerspiegelt, können Sie nur viel Glück wünschen.
▍Abweisung abrufen
Wir haben die
React-Inlinesvg-Bibliothek verwendet , die Styling-Optionen für eingebettete SVG-Bilder und das Zwischenspeichern von Abfragen bietet. Hier hatten wir jedoch ein Problem: Beim Ausführen des Server-Renderings gibt es keine XHR-Anforderungen (zumindest nicht im Sinne von URLs, die von Webpack generiert wurden, wie zu erwarten). Versuche, solche Anforderungen auszuführen, beeinträchtigen das Rendern des Servers.
Obwohl es andere Bibliotheken für die Arbeit mit eingebetteten SVG-Daten gibt, die SSR unterstützen, habe ich beschlossen, diese Funktion aufzugeben, da SVG-Dateien noch selten verwendet wurden. Ich habe sie entweder durch normale Bilder,
<img>
-Tags, ersetzt, wenn ich beim Anzeigen der entsprechenden Bilder kein Styling benötigte, oder ich habe sie in Form von React JSX in den Code eingebettet. Wahrscheinlich ist jetzt alles besser geworden, da die JSX-Illustrationen jetzt beim ersten Laden der Seite in den Browser kamen und das an den Client gesendete JS-Bundle 1 Bibliothek weniger hatte.
Wenn Sie
next.config.js
verwenden
whatwg-fetch
(ich brauchte diese Funktion für eine andere Bibliothek), können Sie dies mit
next.config.js
konfigurieren, indem Sie
whatwg-fetch
und
node-fetch
:
module.exports = { webpack: (config, options) => Object.assign(config, { plugins: config.plugins.concat([ new webpack.ProvidePlugin( config.isServer ? {} : { fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch' } ), ]), resolve: Object.assign(config.resolve, { alias: Object.assign( config.resolve.alias, config.isServer ? {} : { fetch: 'node-fetch' } ), }), }), };
▍ Client und Server JS
Die letzte Funktion von Next.js, die ich hier erwähnen möchte, ist, dass dieses Framework zweimal gestartet wird - einmal für den Server und erneut für den Client. Dadurch wird die Grenze zwischen clientseitigem JavaScript und Node.js-Code in derselben Codebasis leicht verwischt, was zu ungewöhnlichen Fehlern wie
fs is undefined
Dies
fs is undefined
wenn versucht wird, die Funktionen von Node.js auf dem Client zu nutzen.
Daher müssen wir solche Konstruktionen in
next.js.config
:
module.exports = { webpack: (config, options) => Object.assign(config, { node: config.isServer ? undefined : { fs: 'empty' }, }), };
Das Flag
config.isServer
in Webpack ist Ihr bester Freund, wenn Sie denselben Code in verschiedenen Umgebungen ausführen müssen.
Darüber hinaus unterstützt Next.js zusätzlich zu den Standardmethoden für den Lebenszyklus von React-Komponenten die Methode
getInitialProps()
, die nur aufgerufen wird, wenn der Code im
getInitialProps()
wird:
class HomePage extends React.Component { static getInitialProps() { // } componentDidMount() { // , } … }
Ja, und vergessen wir nicht, dass unser guter Freund, das
window
, das zum Organisieren des Abhörens von Ereignissen, zum Bestimmen der Größe des Browserfensters und zum Zugriff auf viele nützliche Funktionen erforderlich ist, in Node.js nicht verfügbar ist:
if (typeof window !== 'undefined') { // , `window` }
Es sollte beachtet werden, dass selbst Next.js den Entwickler nicht vor der Notwendigkeit bewahren kann, Probleme zu lösen, die mit der Ausführung desselben Codes auf dem Server und auf dem Client verbunden sind. Bei der Lösung solcher Probleme sind
config.isServer
und
getInitialProps()
jedoch sehr nützlich.
Ergebnisse: Was passiert nach Next.js?
Kurzfristig entspricht das Next.js-Framework hinsichtlich der Leistung perfekt unseren Anforderungen an das Server-Rendering und der Möglichkeit, unsere Website auf Geräten mit deaktiviertem JavaScript anzuzeigen. Außerdem können Sie jetzt erweiterte (Rich-)
Meta-Tags verwenden .
Vielleicht werden wir in Zukunft andere Optionen in Betracht ziehen, falls unsere Anwendung sowohl Server-Rendering als auch komplexere Serverlogik benötigt (zum Beispiel prüfen wir die Möglichkeit der Implementierung der Single Sign-On-Technologie bei manifestold.co und dashboard.manifold.co ) Bis dahin werden wir jedoch Next.js verwenden, da dieses Framework mit geringen Zeitkosten enorme Vorteile brachte.
Liebe Leser! Verwenden Sie Next.js in Ihren Projekten?
