Bei De Voorhoede versuchen wir, die Front-End-Performance für unsere Kunden so weit wie möglich zu steigern. Es ist nicht einfach, jeden Kunden davon zu überzeugen, alle unsere Performance-Richtlinien zu befolgen. Wir versuchen, sie zu überzeugen, indem wir in ihrer Sprache sprechen und die Bedeutung von Performance für die Konversion erklären oder ihre Performance mit der ihrer Hauptkonkurrenten vergleichen.
Nebenbei haben wir kürzlich unsere Website aktualisiert. Abgesehen von einer kompletten Überarbeitung des Designs war dies die ideale Gelegenheit, die Performance auf das Maximum zu pushen. Unser Ziel war es, die Kontrolle zu übernehmen, uns auf die Performance zu konzentrieren, flexibel für die Zukunft zu sein und das Schreiben von Inhalten für unsere Website unterhaltsam zu gestalten. Hier erfahren Sie, wie wir die Front-End-Performance für unsere Website gemeistert haben. Viel Spaß!

Design für Performance
In unseren Projekten führen wir tägliche Diskussionen mit Designern und Produktverantwortlichen über die Balance zwischen Ästhetik und Performance. Für unsere eigene Website war das einfach. Wir glauben, dass eine gute Benutzererfahrung damit beginnt, Inhalte so schnell wie möglich bereitzustellen. Das bedeutet Performance > Ästhetik.
Gute Inhalte, Layouts, Bilder und Interaktivität sind für die Einbindung Ihres Publikums unerlässlich, aber jedes dieser Elemente hat Auswirkungen auf die Ladezeit der Seite und das Endnutzererlebnis. Bei jedem Schritt haben wir überlegt, wie wir eine gute Benutzererfahrung und ein schönes Design erzielen und gleichzeitig minimale Auswirkungen auf die Performance haben können.
Inhalt zuerst
Wir möchten unseren Besuchern den Kerninhalt (Text mit dem wesentlichen HTML und CSS) so schnell wie möglich zur Verfügung stellen. Jede Seite sollte dem primären Zweck des Inhalts dienen: die Botschaft zu vermitteln. Erweiterungen, d. h. JavaScript, vollständiges CSS, Webfonts, Bilder und Analysen, sind dem Kerninhalt untergeordnet.
Kontrolle übernehmen
Nachdem wir die Standards für unsere ideale Website definiert hatten, kamen wir zu dem Schluss, dass wir die volle Kontrolle über jeden Aspekt der Website benötigen. Wir entschieden uns, unseren eigenen statischen Website-Generator zu entwickeln, einschließlich einer Asset-Pipeline, und ihn selbst zu hosten.
Statischer Website-Generator
Wir haben unseren eigenen statischen Website-Generator in Node.js geschrieben. Er nimmt Markdown-Dateien mit kurzen JSON-Seiten-Metabeschreibungen entgegen, um die gesamte Website-Struktur mit all ihren Assets zu generieren. Er kann auch von einer HTML-Datei zur Einbindung von Seitenspezifischem JavaScript begleitet werden.
Unten sehen Sie eine vereinfachte Metabeschreibung und eine Markdown-Datei für einen Blogbeitrag, die zur Generierung des tatsächlichen HTML verwendet werden.
Die JSON-Metabeschreibung
{
"keywords": ["performance", "critical rendering path", "static site", "..."],
"publishDate": "2016-08-12",
"authors": ["Declan"]
}
Und die Markdown-Datei
# A case study on boosting front-end performance
At [De Voorhoede](https://www.voorhoede.nl/en/) we try to boost front-end performance...
## Design for performance
In our projects we have daily discussions...
Bildlieferung
Die durchschnittliche Webseite ist satte 2406 KB, davon 1535 KB Bilder. Da Bilder einen so großen Teil der durchschnittlichen Website ausmachen, sind sie auch eines der besten Ziele für Performance-Gewinne.

WebP
WebP ist ein modernes Bildformat, das eine überlegene verlustfreie und verlustbehaftete Komprimierung für Bilder im Web bietet. WebP-Bilder können erheblich kleiner sein als Bilder in anderen Formaten: manchmal sind sie bis zu 25 % kleiner als ihr JPEG-Gegenstück. WebP wird oft übersehen und nicht oft verwendet. Zum Zeitpunkt des Schreibens wird WebP nur von Chrome, Opera und Android unterstützt (immer noch über 50 % unserer Nutzer), aber wir können bei Bedarf elegant auf JPG/PNG zurückgreifen.
<picture>-Element
Mit dem picture-Element können wir elegant von WebP auf ein weiter verbreitetes Format wie JPEG zurückgreifen.
<picture>
<source type="image/webp" srcset="image-l.webp" media="(min-width: 640px)">
<source type="image/webp" srcset="image-m.webp" media="(min-width: 320px)">
<source type="image/webp" srcset="image-s.webp">
<source srcset="image-l.jpg" media="(min-width: 640px)">
<source srcset="image-m.jpg" media="(min-width: 320px)">
<source srcset="image-s.jpg">
<img alt="Description of the image" src="image-l.jpg">
</picture>
Wir verwenden picturefill von Scott Jehl, um Browser zu polyfillen, die das <picture>-Element nicht unterstützen, und um ein konsistentes Verhalten über alle Browser hinweg zu erzielen.
Wir verwenden das <img>-Element als Fallback für Browser, die das <picture>-Element und/oder JavaScript nicht unterstützen. Die Verwendung der größten Instanz des Bildes stellt sicher, dass es auch im Fallback-Szenario gut aussieht.
Generate
Während der Ansatz zur Bildlieferung vorhanden war, mussten wir noch herausfinden, wie wir ihn schmerzlos implementieren können. Ich liebe das picture-Element für das, was es kann, aber ich hasse es, den obigen Schnipsel zu schreiben. Besonders, wenn ich ihn beim Schreiben von Inhalten einbeziehen muss. Wir wollen uns nicht mit der Generierung von 6 Instanzen jedes Bildes, der Optimierung der Bilder und dem Schreiben von <picture>-Elementen in unserem Markdown befassen. Also haben wir
- generieren mehrere Instanzen der Originalbilder in unserem Build-Prozess, sowohl im Eingabeformat (JPG, PNG) als auch in WebP. Wir verwenden gulp responsive dafür.
- minimieren die generierten Bilder
- schreiben
in unseren Markdown-Dateien. - verwenden benutzerdefinierte Markdown-Renderer während des Build-Prozesses, um konventionelle Markdown-Bilddeklarationen in vollwertige
<picture>-Elemente zu **kompilieren**.
SVG-Animationen
Wir haben einen eigenständigen Grafikstil für unsere Website gewählt, bei dem SVG-Illustrationen eine wichtige Rolle spielen. Dies haben wir aus mehreren Gründen getan.
- Erstens sind SVGs (Vektorbilder) tendenziell kleiner als Bitmap-Bilder;
- Zweitens sind SVGs von Natur aus reaktionsfähig und skalieren perfekt, während sie immer gestochen scharf bleiben. Kein Bedarf mehr an Bildgenerierung und
<picture>-Elementen; - Zu guter Letzt können wir sie mit CSS animieren und verändern! Ein perfektes Beispiel für Design für Performance. Alle unsere Portfolioseiten haben eine individuell gestaltete animierte SVG, die auf der Übersichtsseite wiederverwendet wird. Sie dient als wiederkehrender Stil für alle unsere Portfolioelemente, was das Design konsistent macht und nur geringe Auswirkungen auf die Performance hat.
Schauen Sie sich diese Animation an und wie wir sie mit CSS verändern können.
Siehe den Pen Ändern Sie die Inline-SVG-Stile von De Voorhoede (@voorhoede) auf CodePen.
Benutzerdefinierte Webfonts
Bevor wir eintauchen, hier eine kurze Einführung in das Verhalten von Browsern in Bezug auf benutzerdefinierte Webfonts. Wenn der Browser auf eine @font-face-Definition in CSS stößt, die auf eine Schriftart verweist, die nicht auf dem Computer des Benutzers verfügbar ist, versucht er, diese Schriftartdatei herunterzuladen. Während des Downloads zeigt der Browser den Text mit dieser Schriftart meist nicht an. Gar nicht. Dieses Phänomen wird als "Flash of Invisible Text" oder FOIT bezeichnet. Wenn Sie wissen, wonach Sie suchen müssen, werden Sie es fast überall im Web finden. Und wenn Sie mich fragen, ist es schlecht für das Endnutzererlebnis. Es verzögert den Benutzer bei der Erreichung seines Kernziels: das Lesen des Inhalts.
Wir können den Browser jedoch zwingen, sein Verhalten in ein "Flash of Unstyled Content" oder FOUT zu ändern. Wir weisen den Browser an, zunächst eine allgegenwärtige Schriftart wie Arial oder Georgia zu verwenden. Sobald die benutzerdefinierte Webfont heruntergeladen ist, ersetzt sie die Standardschriftart und rendert den gesamten Text neu. Wenn die benutzerdefinierte Schriftart nicht geladen werden kann, ist der Inhalt immer noch perfekt lesbar. Während einige dies als Fallback betrachten mögen, sehen wir benutzerdefinierte Schriften als Verbesserung. Selbst ohne sie sieht die Website gut aus und funktioniert zu 100 %.

Die Verwendung benutzerdefinierter Webfonts kann das Benutzererlebnis verbessern, solange Sie sie optimieren und verantwortungsvoll bereitstellen.
Schriftart-Subsetting
Subsetting ist mit Abstand der schnellste Weg zur Verbesserung der Webfont-Performance. Ich würde es jedem Webentwickler empfehlen, der benutzerdefinierte Schriftarten verwendet. Sie können beim Subsetting alles geben, wenn Sie die vollständige Kontrolle über den Inhalt haben und wissen, welche Zeichen angezeigt werden. Aber selbst das Subsetting Ihrer Schriftart auf "westliche Sprachen" hat einen enormen Einfluss auf die Dateigröße. Zum Beispiel verringert sich unsere Noto Regular WOFF-Schriftart, die standardmäßig 246 KB groß ist, auf 31 KB, wenn sie auf westliche Sprachen gesubsettet wird. Wir haben den Font Squirrel Webfont-Generator verwendet, der wirklich einfach zu bedienen ist.
Font Face Observer
Font face observer von Bram Stein ist ein großartiges Hilfsskript, um zu überprüfen, ob Schriftarten geladen sind. Es ist unabhängig davon, wie Sie Ihre Schriftarten laden, sei es über einen Webfont-Dienst oder durch Selbst-Hosting. Nachdem das Font Face Observer-Skript uns benachrichtigt hat, dass alle benutzerdefinierten Webfonts geladen sind, fügen wir dem <html>-Element eine Klasse fonts-loaded hinzu. Wir stylen unsere Seiten entsprechend.
html {
font-family: Georgia, serif;
}
html.fonts-loaded {
font-family: Noto, Georgia, serif;
}
Hinweis: Aus Gründen der Kürze habe ich die @font-face-Deklaration für Noto im obigen CSS nicht aufgenommen.
Wir setzen auch einen Cookie, um uns daran zu erinnern, dass alle Schriftarten geladen sind und sich daher im Cache des Browsers befinden. Wir verwenden diesen Cookie für wiederholte Aufrufe, was ich später erklären werde.
In naher Zukunft werden wir Bram Steins JavaScript wahrscheinlich nicht mehr benötigen, um dieses Verhalten zu erreichen. Die CSS Working Group hat einen neuen @font-face-Deskriptor (genannt font-display) vorgeschlagen, bei dem der Wert der Eigenschaft steuert, wie eine herunterladbare Schriftart vor dem vollständigen Laden gerendert wird. Die CSS-Anweisung font-display: swap; würde uns das gleiche Verhalten wie der obige Ansatz bieten. Lesen Sie mehr über die font-display-Eigenschaft.
Lazy Load JS und CSS
Generell verfolgen wir einen Ansatz, Assets so schnell wie möglich zu laden. Wir eliminieren Render-Blocking-Anfragen und optimieren für die erste Ansicht, wobei wir den Browser-Cache für wiederholte Ansichten nutzen.
Lazy Load JS
Konzeptionell haben wir nicht viel JavaScript auf unserer Website. Für das, was wir haben, und was wir in Zukunft verwenden wollen, haben wir einen JavaScript-Workflow entwickelt.
JavaScript im <head> blockiert das Rendering, und das wollen wir nicht. JavaScript sollte nur die Benutzererfahrung verbessern; es ist für unsere Besucher nicht kritisch. Der einfache Weg, Render-Blocking-JavaScript zu beheben, ist, das Skript am Ende Ihrer Webseite zu platzieren. Der Nachteil ist, dass der Download des Skripts erst beginnt, nachdem das gesamte HTML heruntergeladen wurde.
Eine Alternative könnte sein, das Skript in den Head einzufügen und die Skriptausführung zu verzögern, indem Sie das defer-Attribut zum <script>-Tag hinzufügen. Dies macht das Skript nicht-blockierend, da der Browser es fast sofort herunterlädt, ohne den Code auszuführen, bis die Seite geladen ist.
Es bleibt nur noch eines zu erwähnen: Wir verwenden keine Bibliotheken wie jQuery, und daher hängt unser JavaScript von nativen JavaScript-Funktionen ab. Wir möchten JavaScript nur in Browsern laden, die diese Funktionen unterstützen (d. h. Mustard Cutting). Das Endergebnis sieht so aus:
<script>
// Mustard Cutting
if ('querySelector' in document && 'addEventListener' in window) {
document.write('<script src="index.js" defer><\/script>');
}
</script>
Wir platzieren dieses kleine Inline-Skript im Head unserer Seite, das erkennt, ob die nativen JavaScript-Funktionen document.querySelector und window.addEventListener unterstützt werden. Wenn ja, laden wir das Skript, indem wir das script-Tag direkt auf die Seite schreiben, und verwenden das defer-Attribut, um es nicht-blockierend zu machen.
Lazy Load CSS
Für die erste Ansicht ist die größte Render-Blocking-Ressource für unsere Website CSS. Browser verzögern das Rendern der Seite, bis die vollständige CSS-Datei, auf die im <head> verwiesen wird, heruntergeladen und geparst ist. Dieses Verhalten ist beabsichtigt, da der Browser sonst Layouts und Neudrucke während des Renderns ständig neu berechnen müsste.
Um zu verhindern, dass CSS das Rendering blockiert, müssen wir die CSS-Datei asynchron laden. Wir verwenden die großartige loadCSS-Funktion von Filament Group. Sie gibt uns einen Callback, wenn die CSS-Datei geladen ist, und wir setzen dann einen Cookie, der besagt, dass das CSS geladen ist. Diesen Cookie verwenden wir für wiederholte Aufrufe, was ich später erklären werde.
Beim asynchronen Laden von CSS gibt es ein "Problem": Während das HTML sehr schnell gerendert wird, sieht es wie reines HTML ohne angewendetes CSS aus, bis das vollständige CSS heruntergeladen und geparst ist. Hier kommt Critical CSS ins Spiel.
Critical CSS
Critical CSS kann als die minimale Menge an blockierendem CSS beschrieben werden, um eine Seite für den Benutzer erkennbar erscheinen zu lassen. Wir konzentrieren uns auf Inhalte "above the fold" (oberhalb des sichtbaren Bereichs). Offensichtlich variiert die Position des Falzes je nach Gerät stark, daher machen wir eine bestmögliche Schätzung.
Die manuelle Bestimmung dieses kritischen CSS ist ein zeitaufwändiger Prozess, insbesondere bei zukünftigen Stiländerungen. Es gibt mehrere nützliche Skripte zur Generierung von Critical CSS in Ihrem Build-Prozess. Wir haben das prächtige critical npm-Modul von Addy Osmani verwendet.
Sehen Sie unten unsere Homepage, gerendert mit Critical CSS und gerendert mit dem vollständigen CSS. Beachten Sie den Fold, unter dem die Seite immer noch etwas ungestaltet ist.

Der Server
Wir hosten die Website von De Voorhoede selbst, da wir die Kontrolle über die Serverumgebung haben wollten. Wir wollten auch experimentieren, wie wir die Performance durch Änderung der Serverkonfiguration steigern können. Derzeit haben wir einen Apache-Webserver und servieren unsere Website über HTTPS.
Konfiguration
Um Performance und Sicherheit zu steigern, haben wir uns etwas recherchiert, wie wir den Server konfigurieren können.
Wir verwenden die H5BP Boilerplate Apache-Konfiguration, die ein guter Ausgangspunkt für die Verbesserung von Performance und Sicherheit für Ihren Apache-Webserver ist. Sie haben auch Konfigurationen für andere Serverumgebungen.
Wir haben GZIP für die meisten unserer HTML-, CSS- und JavaScript-Dateien aktiviert. Wir haben Caching-Header für alle unsere Ressourcen sorgfältig eingestellt. Lesen Sie dazu weiter unten im Abschnitt Dateiebene-Caching.
HTTPS
Das Ausliefern Ihrer Website über HTTPS kann Auswirkungen auf die Performance Ihrer Website haben. Die Leistungseinbuße liegt hauptsächlich in der Einrichtung des SSL-Handshakes, was zu viel Latenz führt. Aber – wie immer – können wir etwas dagegen tun!
HTTP Strict Transport Security ist ein HTTP-Header, mit dem der Server dem Browser mitteilen kann, dass er nur über HTTPS kommunizieren soll. So werden HTTP-Anfragen daran gehindert, zu HTTPS weitergeleitet zu werden. Alle Versuche, auf die Website über HTTP zuzugreifen, sollten automatisch umgewandelt werden. Das spart uns einen Roundtrip!
TLS False Start ermöglicht es dem Client, verschlüsselte Daten sofort nach dem ersten TLS-Roundtrip zu senden. Diese Optimierung reduziert den Handshake-Overhead für neue TLS-Verbindungen auf einen Roundtrip. Sobald der Client den Verschlüsselungsschlüssel kennt, kann er mit der Übertragung von Anwendungsdaten beginnen. Der Rest des Handshakes dient der Bestätigung, dass niemand die Handshake-Datensätze manipuliert hat, und kann parallel erfolgen.
TLS Session Resumption spart uns einen weiteren Roundtrip, indem sichergestellt wird, dass der Browser und der Server, wenn sie bereits über TLS kommuniziert haben, den Sitzungsidentifikator wiederverwenden können. Beim nächsten Aufbau einer Verbindung kann dieser Identifikator wiederverwendet werden, was einen Roundtrip spart.
Ich klinge wie ein DevOps-Ingenieur, aber das bin ich nicht. Ich habe nur einige Dinge gelesen und Videos gesehen. Ich habe Mythbusting HTTPS: Squashing security’s urban legends von Emily Stark von Google I/O 2016 geliebt.
Verwendung von Cookies
Wir haben keine serverseitige Sprache, nur einen statischen Apache-Webserver. Aber ein Apache-Webserver kann immer noch Server-Side Includes (SSI) durchführen und Cookies auslesen. Durch kluge Nutzung von Cookies und das Ausliefern von HTML, das teilweise von Apache neu geschrieben wird, können wir die Front-End-Performance steigern. Nehmen Sie dieses Beispiel unten (unser tatsächlicher Code ist etwas komplexer, aber es läuft auf dieselben Ideen hinaus)
<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css' )"-->
<noscript><link rel="stylesheet" href="0d82f.css"></noscript>
<script>
(function() {
function loadCSS(url) {...}
function onloadCSS(stylesheet, callback) {...}
function setCookie(name, value, expInDays) {...}
var stylesheet = loadCSS('0d82f.css');
onloadCSS(stylesheet, function() {
setCookie('css-loaded', '0d82f', 100);
});
}());
</script>
<style>/* Critical CSS here */</style>
<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->
Die serverseitige Apache-Logik sind die kommentarähnlichen Zeilen, die mit <!-- # beginnen. Betrachten wir dies Schritt für Schritt:
$HTTP_COOKIE!=/css-loaded/prüft, ob noch kein CSS-Cache-Cookie existiert.$HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css'prüft, ob die gecachte CSS-Version nicht die aktuelle Version ist.- Wenn
<!-- #if expr="..." -->zutrueausgewertet wird, gehen wir davon aus, dass dies die erste Ansicht des Besuchers ist. - Für die erste Ansicht fügen wir einen
<noscript>-Tag mit einem Render-Blocking<link rel="stylesheet">hinzu. Dies tun wir, weil wir das vollständige CSS asynchron mit JavaScript laden werden. Wenn JavaScript deaktiviert wäre, wäre dies nicht möglich. Das bedeutet, dass wir als Fallback CSS "nach Zahlen" laden, d.h. blockierend. - Wir fügen ein Inline-Skript mit Funktionen zum Lazy Loading von CSS, einen
onloadCSS-Callback und setzen Cookies. - Im selben Skript laden wir das vollständige CSS asynchron.
- Im
onloadCSS-Callback setzen wir einen Cookie mit dem Version-Hash als Cookie-Wert. - Nach dem Skript fügen wir ein Inline-Stylesheet mit dem Critical CSS hinzu. Dies wird Render-Blocking sein, aber es wird sehr klein sein und verhindern, dass die Seite als reines, ungestaltetes HTML angezeigt wird.
- Die
<!-- #else -->-Anweisung (was bedeutet, dass dascss-loaded-Cookie **vorhanden ist**) repräsentiert die wiederholten Ansichten des Besuchers. Da wir bis zu einem gewissen Grad davon ausgehen können, dass die CSS-Datei zuvor geladen wurde, können wir den Browser-Cache nutzen und das Stylesheet blockierend ausliefern. Es wird aus dem Cache geladen und lädt fast sofort.
Der gleiche Ansatz wird verwendet, um Schriftarten für die erste Ansicht asynchron zu laden, wobei davon ausgegangen wird, dass wir sie bei wiederholten Ansichten aus dem Browser-Cache liefern können.

Dateiebene-Caching
Da wir uns stark auf Browser-Caching für wiederholte Ansichten verlassen, müssen wir sicherstellen, dass wir richtig cachen. Idealerweise möchten wir Assets (CSS, JS, Fonts, Bilder) für immer cachen und den Cache nur ungültig machen, wenn sich eine Datei tatsächlich ändert. Der Cache wird ungültig gemacht, wenn die Anfrageressource eindeutig ist. Wir verwenden git tag für unsere Website, wenn wir eine neue Version veröffentlichen. Der einfachste Weg wäre also, einen Query-Parameter zu den Anfrageressourcen mit der Codebasis-Version hinzuzufügen, wie z. B. `https://www.voorhoede.nl/assets/css/main.css?v=1.0.4`. Aber.
Der Nachteil dieses Ansatzes ist, dass bei einem neuen Blogbeitrag (der Teil unserer Codebasis ist, nicht extern in einem CMS gespeichert) der Cache für alle unsere Assets ungültig gemacht wird, obwohl keine Änderungen an diesen Assets vorgenommen wurden.
Bei dem Versuch, unseren Ansatz zu verbessern, sind wir auf gulp-rev und gulp-rev-replace gestoßen. Diese Skripte halfen uns, eine Revisionierung pro Datei hinzuzufügen, indem wir unseren Dateinamen einen Content-Hash anhängen. Das bedeutet, dass sich die Anfrageressource nur ändert, wenn die tatsächliche Datei geändert wurde. Jetzt haben wir eine Cache-Invalidierung pro Datei. Das lässt mein Herz höher schlagen!
Ergebnis
Wenn Sie es bis hierher geschafft haben (großartig!), möchten Sie wahrscheinlich das Ergebnis wissen. Das Testen der Performance Ihrer Website kann mit Tools wie PageSpeed Insights für sehr praktische Tipps und WebPagetest für umfassende Netzwerkanalysen erfolgen. Ich denke, der beste Weg, Ihre Rendering-Performance zu testen, ist, Ihre Seite beim Drosseln Ihrer Verbindung wahnsinnig werden zu sehen. Das bedeutet: Drosseln in einer wahrscheinlich unrealistischen Weise. In Google Chrome können Sie Ihre Verbindung drosseln (über den Inspektor > Netzwerk-Tab) und sehen, wie Anfragen langsam geladen werden, während Ihre Seite aufgebaut wird.
Sehen Sie also hier, wie unsere Homepage auf einer gedrosselten GPRS-Verbindung von 50 KB/s lädt.

Beachten Sie, wie wir den ersten Render bei 2,27 s auf einem 50 KB/s GPRS-Netzwerk erhalten, dargestellt durch das erste Bild aus dem Filmstreifen und die entsprechende gelbe Linie in der Wasserfallansicht. Die gelbe Linie wird direkt nach dem Download des HTML gezeichnet. Das HTML enthält das Critical CSS, das sicherstellt, dass die Seite brauchbar aussieht. Alle anderen blockierenden Ressourcen werden lazy geladen, sodass wir mit der Seite interagieren können, während der Rest heruntergeladen wird. Das ist genau das, was wir wollten!
Eine weitere Sache, die Sie bemerken sollten, ist, dass benutzerdefinierte Schriftarten bei so langsamen Verbindungen nie geladen werden. Der Font Face Observer kümmert sich automatisch darum, aber wenn wir die Schriftarten nicht asynchron laden würden, würden Sie in den meisten Browsern eine Weile auf FOIT starren.
Die vollständige CSS-Datei wird erst nach 8 Sekunden geladen. Umgekehrt, wenn wir das vollständige CSS blockierend laden würden, anstatt Critical CSS inline zu haben, würden wir 8 Sekunden lang auf eine weiße Seite starren.
Wenn Sie neugierig sind, wie sich diese Zeiten mit anderen Websites vergleichen, die sich weniger auf die Performance konzentrieren, nur zu. Die Ladezeiten werden durch die Decke gehen!
Der Test unserer Website gegen die zuvor erwähnten Tools zeigt ebenfalls einige schöne Ergebnisse. PageSpeed Insights gibt uns eine 100/100-Bewertung für die mobile Performance, wie großartig ist das denn?!

Wenn wir uns WebPagetest ansehen, erhalten wir das folgende Ergebnis:

Wir können sehen, dass unser Server gut funktioniert und der SpeedIndex für die erste Ansicht 693 beträgt. Das bedeutet, dass unsere Seite nach 693 ms über eine Kabelverbindung nutzbar ist. Sieht gut aus!
Roadmap
Wir sind noch nicht fertig und entwickeln unseren Ansatz ständig weiter. In naher Zukunft werden wir uns auf Folgendes konzentrieren:
- HTTP/2: Es ist da und wir experimentieren gerade damit. Viele der in diesem Artikel beschriebenen Dinge sind bewährte Praktiken, die auf den Einschränkungen von HTTP/1.1 basieren. Kurz gesagt: HTTP/1.1 stammt aus dem Jahr 1999, als Tabellenlayouts und Inline-Styles super angesagt waren. HTTP/1.1 war nie für 2,6 MB große Webseiten mit 200 Anfragen konzipiert. Um die Schmerzen unseres alten Protokolls zu lindern, verketten wir JS und CSS, binden kritisches CSS inline ein, verwenden Daten-URLs für kleine Bilder usw. Alles, um Anfragen zu sparen. Da HTTP/2 mehrere Anfragen parallel über dieselbe TCP-Verbindung ausführen kann, könnten diese Verkettungen und die Reduzierung von Anfragen sogar ein Antipattern sein. Wir werden zu HTTP/2 wechseln, sobald wir mit den Experimenten fertig sind.
- Service Workers: Dies ist eine moderne Browser-JavaScript-API, die im Hintergrund läuft. Sie ermöglicht viele Funktionen, die für Websites bisher nicht verfügbar waren, wie z. B. Offline-Unterstützung, Push-Benachrichtigungen, Hintergrundsynchronisierung und mehr. Wir spielen mit Service Workers herum, müssen sie aber noch in unsere eigene Website integrieren. Ich garantiere Ihnen, wir werden es tun!
- CDN: Wir wollten die Kontrolle behalten und haben die Website selbst gehostet. Ja, ja, und jetzt wollen wir zu einem CDN wechseln, um die Netzwerklatenz zu beseitigen, die durch die physische Entfernung zwischen Client und Server verursacht wird. Obwohl unsere Kunden hauptsächlich in den Niederlanden ansässig sind, möchten wir die weltweite Front-End-Community auf eine Weise erreichen, die widerspiegelt, was wir am besten können: Qualität, Leistung und die Weiterentwicklung des Webs.
Danke fürs Lesen! Besuchen Sie unsere Website, um das Endergebnis zu sehen. Haben Sie Kommentare oder Fragen? Lassen Sie es uns über Twitter wissen. Und wenn Sie gerne schnelle Websites bauen, warum schließen Sie sich uns nicht an?
Ich würde gerne einen Folgeartikel mit den Ergebnissen Ihrer HTTP/2-Tests sehen.
Es wird eine Fortsetzung geben! Wir arbeiten in Komponenten, sodass wir die verkettete CSS-Datei zum Beispiel recht einfach in mehrere Komponenten-CSS-Dateien aufteilen können. Was ich Ihnen sagen kann, ist, dass wir bisher keine HTTP/2-Konfiguration gefunden haben, die unser hier beschriebenes, für HTTP/1 optimiertes Setup schlägt, was irgendwie seltsam ist.
Wenn Sie nur statische Inhalte bereitstellen. Warum Apache verwenden? Nginx ist für diese Art von Aufgabe besser geeignet.
Sie haben absolut Recht, wir versuchen so schnell wie möglich umzusteigen.
Ich stecke gerade in diesem Dilemma für meine eigene Website, aber das Problem, das ich habe, ist, dass das HTTP/2-Modul von Nginx Server Push nicht unterstützt:\ Bis es das tut, bleibe ich bei Apache 2.4.x.
Das ist ein wirklich solider Artikel. Einer der besten, die ich seit langem gelesen habe. Ich habe das Gefühl, dass jeder gerne über Leistung spricht, aber nur wenige die Schritte unternehmen, um sie zu verbessern. Oder sie konzentrieren sich nur auf das Backend oder nur auf das Frontend. Ich mag den Aspekt des statischen Site-Generators (leider bin ich in der CMS-Welt, daher sehe ich nicht, dass ich jemals diesen Punkt erreiche). Eine Frage zum statischen Site-Generator: Ich habe das Gefühl, Sie haben gerade Ihre eigene Version von Jekyll (oder einem der anderen statischen Site-Generatoren) neu erfunden. Was macht Ihr Generator, was Jekyll für Sie nicht konnte?
Danke Jason!
In unseren Front-End-Projekten schreiben wir Komponenten, die wir wiederverwenden und zu HTML-Vorlagen kombinieren. Daher haben wir viele Build-Aufgaben für die Kompilierung einer statischen Website. Wir brauchten einige zusätzliche Aufgaben, z. B. die Bildgenerierungsaufgabe und das Hashing der statischen Assets für die sofortige Cache-Invalidierung. Aber große Teile des Build-Prozesses werden aus anderen Projekten wiederverwendet.
Außerdem wollten wir die vollständige Kontrolle darüber haben, wie wir Inhalte bearbeiten und optimierte Seiten an unsere Besucher ausliefern. Das hätten wir mit einem bereits vorhandenen statischen Site-Generator tun können, aber das ist unsere Architektur. Alle meine Kollegen können den Generator recht einfach erweitern, da sie mit dem Setup vertraut sind. Er tut nur das, was er tun muss, wir haben während der Entwicklung iterativ zu diesem Punkt entwickelt. Wenn Sie also den Generator erweitern, müssen Sie nicht durch ungenutzten Ballast blättern. Aber hey, Jekyll und andere sind großartig! Wahrscheinlich das richtige Werkzeug für den Job.
Guter Artikel, Declan!
Wir haben eine Reihe ähnlicher Verbesserungen an unserer Website vorgenommen – wir haben eine Punktzahl von 97/98 für Mobil- und PC-Ansichten im PageSpeed Score erreicht https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Fonedollarclub.com.ua%2Fru%2F
HTTPS/2 ist ein wirklich nettes Feature. Ich hoffe, das hilft Ihnen weiter.
Wir werden die WebP-Integration prüfen, danke für die Idee
Danke Igor! Und gute Arbeit!
Sehr spannender Artikel, ich wäre daran interessiert zu sehen, wie die Webseite im Kontrast zu Ihren Fallback-Bedingungen abschneidet. Ich habe oft das Gefühl, dass „graceful degradation“ nur ein höflicher Begriff dafür ist, Endbenutzer zu mobben, ihr Client zu aktualisieren.
Ich würde gerne Ihren Ansatz für CDN sehen. Sie erwähnten, dass Ihr Ziel darin besteht, Latenz und Paketverlust aufgrund einer kürzeren Entfernung zum POP zu reduzieren. Aber wird die Lokalisierung Teil dieser Überlegung sein? Haben Sie Bedenken hinsichtlich der Synchronisation mit Ihrem aktuellen Ansatz für die Dateicaching?
Da wir technisch kein statisches HTML haben (wegen der serverseitigen Cookie-Prüfung), können wir kein HTML cachen. Das bedeutet, dass wir nur unsere statischen Assets über ein CDN ausliefern können. Die Cache-Invalidierung würde also genauso funktionieren wie jetzt (durch eindeutigen Dateinamen) und wir würden nur die Netzwerklatenz für unsere statischen Assets reduzieren. Wir wollen immer noch jegliche Netzwerklatenz loswerden, daher prüfen wir die Bereitstellung unserer Website an einigen Edge-Standorten, was bedeutet, dass unser HTML von einem näheren Standort ausgeliefert wird (statische Assets weiterhin über CDN).
Was ist Ihre Meinung zur Leistungseinbuße durch SVG-Speicherverbrauch?
So sehr ich die reaktive Natur von SVG und die einfache Manipulation für Animationen oder Theming mag, stelle ich fest, dass eine starke Nutzung von SVGs den Rest einer Seite belasten kann, da mehr benötigt wird, um ein SVG anzuzeigen als ein einfaches Bitmap-Bild.
Vielleicht bin ich aber der Einzige, der das so wahrnimmt?
Ich weiß nicht, ob ich vollständig verstehe, was Sie meinen, aber da SVG, das im HTML inline ist, nur DOM-Knoten sind, kann das DOM unübersichtlich werden und die Animation kann ruckelig werden, wenn Ihr SVG zu komplex ist. Dies kann passieren, wenn Sie beispielsweise eine große Netzwerkvirtualisierung mit Beziehungen zwischen Entitäten rendern. Dieses Problem reduziert sich auf die Komplexität Ihres SVG. Sie können in diesem Fall Canvas verwenden, da dies nur ein DOM-Knoten ist. Haben Sie das bei den Animationen auf unserer Website erlebt? Habe ich Ihre Frage beantwortet?
Wow, toller Artikel, tolle Website! Nur eine Frage: Warum haben Sie sich entschieden, ein animiertes .GIF auf der Homepage im Hero-Bereich zu verwenden, wenn Sie an anderer Stelle auf der Website SVGs verwenden?
Hah, Sie haben mich erwischt! Wir müssen das noch in SVG konvertieren, wir arbeiten daran.
@Declan 1000+ Großartiger Artikel, den ich je gelesen habe. Danke für das Teilen dieses wundervollen Artikels. Er ist ein Muss für alle.
Danke!
Das ist großartig und scheint die Breite und Tiefe der Front-End-Performance-Techniken gut zusammenzufassen. Ich bin auch ein wenig überrascht, dass der Code der Website nicht als Optimierungsmöglichkeit aufgenommen wurde. Insbesondere CSS, das je nach Faktorisierung unterschiedlich schnell geparst werden kann. Gibt es hierzu Erkenntnisse bezüglich Ihrer Website, die Sie teilen können?
Ich habe versucht, den Artikel auf die Optimierung der wahrgenommenen Leistung zu konzentrieren. Mit diesem Setup bestimmen die Größe Ihres kritischen CSS, die Größe Ihres HTML und die First-Byte-Zeit weitgehend, wie schnell Sie eine nutzbare Seite ausliefern können. Ich würde sagen, je größer Ihr CSS und JS ist, desto mehr profitiert die Leistung von diesem Ansatz.
Wir minimieren CSS, JS, sogar HTML, wir verwenden kein jQuery und progressive enhancement; wir arbeiten in Komponenten, sodass HTML, CSS und JS einfach zu warten sind. Dies alles kommt der Größe unserer Assets zugute. Aber auch hier profitiert die Gesamtladezeit und nicht unbedingt die erste Darstellungszeit.
Sehr schöner Artikel.
Mir scheint, wenn Cookies verwendet werden, wird auch bei der Anforderung einer statischen Datei (.jpg, .css…) der Cookie mitgesendet.
Wenn Sie sagen, Sie werden einen CDN-Server verwenden, ist das, um einen Cookieless-Server zur Speicherung von Asset-Dateien zu haben?
Werden Sie dies mit der Verwendung von „dns-prefetching“ kombinieren?
Ich kann den nächsten Artikel über das Update kaum erwarten.
In einem der obigen Kommentare habe ich erklärt, dass wir technisch gesehen keine statische Website haben. Wir müssen die Cookies nur im HTML lesen, das wir nicht über ein CDN ausliefern. Für die statischen Assets, die wir über ein CDN ausliefern, interessieren uns die Cookies nicht. Ich empfehle dringend DNS-Prefetching bei Verwendung eines CDN.
Sie sollten sich http://surge.sh als CDN-Option ansehen. Ich habe sie noch nicht in der Produktion verwendet, aber sie scheinen eine gute, wartungsarme Option zu sein.
Danke für den großartigen Artikel. Front-End-Performance ist ein Thema, dem ich schon seit einiger Zeit folge und ich versuche ständig, die Leistung für die Projekte meines Unternehmens so weit wie möglich zu optimieren.