In diesem Codelab wird untersucht, wie durch das Komprimieren und Komprimieren des JavaScript-Bundles für die folgende Anwendung die Seitenleistung verbessert wird, indem die Anfragegröße der Anwendung reduziert wird.
Messen
Bevor Sie Optimierungen hinzufügen, sollten Sie immer zuerst den aktuellen Status der Anwendung analysieren.
- Wenn Sie sich eine Vorschau der Website ansehen möchten, klicken Sie auf App ansehen und dann auf Vollbild .
Mit dieser App, die auch im Codelab "Remove unused code" beschrieben wurde, können Sie für Ihr Lieblingskätzchen stimmen. 🐈
Sehen wir uns nun an, wie groß diese Anwendung ist:
- Drücken Sie „Strg + Umschalttaste + J“ (oder „Befehlstaste + Wahltaste + J“ auf einem Mac), um die Entwicklertools zu öffnen.
- Klicken Sie auf den Tab Netzwerk.
- Klicken Sie das Kästchen Cache deaktivieren an.
- Aktualisieren Sie die App.
Obwohl im Codelab "Nicht verwendeten Code entfernen" bereits große Fortschritte gemacht wurden, um die Bundle-Größe zu reduzieren, sind 225 KB immer noch recht groß.
Minimierung
Betrachten Sie den folgenden Codeblock.
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
Wenn diese Funktion in einer eigenen Datei gespeichert wird, beträgt die Dateigröße etwa 112 B (Byte).
Wenn alle Leerzeichen entfernt werden, sieht der resultierende Code so aus:
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
Die Dateigröße würde nun etwa 83 B betragen. Wenn er durch Reduzieren der Länge des Variablennamens und Ändern einiger Ausdrücke weiter verzerrt wird, kann der endgültige Code so aussehen:
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
Die Dateigröße erreicht jetzt 62 B.
Mit jedem Schritt wird der Code schwieriger zu lesen. Das JavaScript-Modul des Browsers interpretiert diese jedoch jeweils auf die gleiche Weise. Die Vorteile einer solchen Verschleierung von Code können dazu beitragen, kleinere Dateigrößen zu erreichen. 112 B war eigentlich nicht viel für den Anfang, aber die Größe verringerte sich dennoch um 50 %.
In dieser Anwendung wird webpack Version 4 als Modul-Bundler verwendet. Die jeweilige Version finden Sie unter package.json
.
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
Version 4 reduziert das Bundle bereits standardmäßig im Produktionsmodus. Dabei wird das Plug-in TerserWebpackPlugin
für Terser verwendet.
Terser ist ein beliebtes Tool zur Komprimierung von JavaScript-Code.
Wenn Sie eine Vorstellung davon bekommen möchten, wie der komprimierte Code aussieht, klicken Sie im Bereich Netzwerk der Entwicklertools auf main.bundle.js
. Klicken Sie jetzt auf den Tab Response (Antwort).
Der Code wird in seiner endgültigen Form, reduziert und manipuliert, im Antworttext angezeigt.
Um herauszufinden, wie groß das Bundle möglicherweise gewesen wäre, wenn es nicht minimiert wurde, öffnen Sie webpack.config.js
und aktualisieren Sie die mode
-Konfiguration.
module.exports = {
mode: 'production',
mode: 'none',
//...
Aktualisieren Sie die Anwendung und sehen Sie sich die Bundle-Größe noch einmal im Bereich Netzwerk der Entwicklertools an.
Das ist ein ziemlich großer Unterschied! 😅
Machen Sie die Änderungen hier rückgängig, bevor Sie fortfahren.
module.exports = {
mode: 'production',
mode: 'none',
//...
Ob Sie einen Prozess zum Reduzieren des Codes in Ihrer Anwendung einbinden, hängt von den verwendeten Tools ab:
- Bei Verwendung von Webpack v4 oder höher ist kein zusätzlicher Aufwand erforderlich, da Code im Produktionsmodus standardmäßig reduziert wird. 👍
- Wenn eine ältere Version des Webpacks verwendet wird, installieren Sie
TerserWebpackPlugin
und fügen Sie sie in den Webpack-Build-Prozess ein. In der Dokumentation wird dies ausführlich erläutert. - Es gibt auch andere Reduzierungs-Plug-ins, die stattdessen verwendet werden können, z. B. BabelMinifyWebpackPlugin und ClosureCompilerPlugin.
- Wenn ein Modul-Bundler überhaupt nicht verwendet wird, verwenden Sie Terser als CLI-Tool oder schließen Sie ihn direkt als Abhängigkeit ein.
Komprimierung
Auch wenn der Begriff "Komprimierung" manchmal locker verwendet wird, um zu erklären, wie Code während der Reduzierung reduziert wird, wird er nicht im wörtlichen Sinne komprimiert.
Komprimierung bezieht sich in der Regel auf Code, der mit einem Datenkomprimierungsalgorithmus geändert wurde. Im Gegensatz zur Komprimierung, die schließlich perfekt gültigen Code liefert, muss komprimierter Code vor der Verwendung dekomprimiert werden.
Bei jeder HTTP-Anfrage und -Antwort können Browser und Webserver headers hinzufügen, um zusätzliche Informationen zu dem abzurufenden oder empfangenen Asset anzugeben. Sie sehen dies auf dem Tab Headers
im Bereich „Netzwerk“ der Entwicklertools. Dort werden drei Typen angezeigt:
- General steht für allgemeine Header, die für die gesamte Anfrage-Antwort-Interaktion relevant sind.
- Unter Antwortheader wird eine Liste von Headern angezeigt, die für die tatsächliche Antwort des Servers spezifisch sind.
- Anfrageheader zeigt eine Liste der Header an, die der Anfrage vom Client angehängt wurden.
Sehen Sie sich die accept-encoding
-Kopfzeile in Request Headers
an.
Mit accept-encoding
wird vom Browser angegeben, welche Inhaltscodierungsformate oder Komprimierungsalgorithmen unterstützt werden. Es gibt viele Algorithmen zur Textkomprimierung, aber nur drei werden hier für die Komprimierung (und Dekomprimierung) von HTTP-Netzwerkanfragen unterstützt:
- Gzip (
gzip
): Das am häufigsten verwendete Komprimierungsformat für Server- und Clientinteraktionen. Es baut auf dem Deflate-Algorithmus auf und wird von allen aktuellen Browsern unterstützt. - Deflate (
deflate
): Nicht häufig verwendet. - Brotli (
br
): Ein neuerer Komprimierungsalgorithmus, mit dem die Komprimierungsverhältnisse weiter verbessert werden sollen, was zu einem noch kürzeren Laden der Seiten führen kann. Sie wird von den neuesten Versionen der meisten Browser unterstützt.
Die Beispielanwendung in dieser Anleitung ist mit der App aus dem Codelab "Remove unused code" (Nicht verwendeten Code entfernen) identisch. Der einzige Unterschied besteht darin, dass jetzt Express als Server-Framework verwendet wird. In den nächsten Abschnitten geht es um die statische und die dynamische Komprimierung.
Dynamische Komprimierung
Bei der dynamischen Komprimierung werden Assets sofort dann komprimiert, wenn sie vom Browser angefordert werden.
Vorteile
- Das Erstellen und Aktualisieren von gespeicherten komprimierten Versionen von Assets ist nicht erforderlich.
- Die Komprimierung im laufenden Betrieb funktioniert besonders gut bei dynamisch generierten Webseiten.
Nachteile
- Es dauert länger, Dateien auf höheren Ebenen zu komprimieren, um bessere Komprimierungsverhältnisse zu erzielen. Dies kann zu Leistungseinbußen führen, da der Nutzer auf die Komprimierung der Assets wartet, bevor sie vom Server gesendet werden.
Dynamische Komprimierung mit Node/Express
Die Datei server.js
ist für die Einrichtung des Knotenservers verantwortlich, auf dem die Anwendung gehostet wird.
const express = require('express');
const app = express();
app.use(express.static('public'));
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
Aktuell importieren Sie lediglich express
und verwenden die Middleware express.static
, um alle statischen HTML-, JS- und CSS-Dateien im Verzeichnis public/
zu laden. Diese Dateien werden mit jedem Build von Webpack erstellt.
Damit alle Assets bei jeder Anfrage komprimiert werden, kann die Middleware-Bibliothek für Komprimierung verwendet werden. Fügen Sie es zuerst als devDependency
in package.json
hinzu:
"devDependencies": {
//...
"compression": "^1.7.3"
},
Importieren Sie es in die Serverdatei server.js
:
const express = require('express');
const compression = require('compression');
Fügen Sie sie als Middleware hinzu, bevor express.static
bereitgestellt wird:
//...
const app = express();
app.use(compression());
app.use(express.static('public'));
//...
Aktualisieren Sie nun die App und sehen Sie sich im Bereich Netzwerk die Bundle-Größe an.
Von 225 KB auf 61,6 KB! Im Response Headers
zeigt ein content-encoding
-Header an, dass der Server diese mit gzip
codierte Datei heruntersendet.
Statische Komprimierung
Die Idee hinter der statischen Komprimierung besteht darin, die Assets vorab zu komprimieren und zu speichern.
Vorteile
- Die Latenz aufgrund der hohen Komprimierungsstufen spielt keine Rolle mehr. Die Komprimierung von Dateien muss nicht spontan erfolgen, da sie jetzt direkt abgerufen werden können.
Nachteile
- Assets müssen bei jedem Build komprimiert werden. Die Build-Dauer kann sich bei einer hohen Komprimierung erheblich verlängern.
Statische Komprimierung mit Node/Express und Webpack
Da bei der statischen Komprimierung Dateien im Voraus komprimiert werden, können die Webpack-Einstellungen so geändert werden, dass Assets im Rahmen des Build-Schritts komprimiert werden.
CompressionPlugin
kann dazu verwendet werden.
Fügen Sie es zuerst als devDependency
in package.json
hinzu:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
Importieren Sie es wie jedes andere Webpack-Plug-in in die Konfigurationsdatei webpack.config.js:
.
const path = require("path");
//...
const CompressionPlugin = require("compression-webpack-plugin");
Fügen Sie es außerdem in das Array plugins
ein:
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
Das Plug-in komprimiert die Build-Dateien standardmäßig mit gzip
. In der Dokumentation erfahren Sie, wie Sie Optionen hinzufügen, um einen anderen Algorithmus zu verwenden oder bestimmte Dateien ein- oder auszuschließen.
Wenn die App neu geladen und neu erstellt wird, wird jetzt eine komprimierte Version des Haupt-Bundles erstellt. Öffne die Glitch Console, um dir den Inhalt des endgültigen public/
-Verzeichnisses anzusehen, das vom Knotenserver bereitgestellt wird.
- Klicken Sie auf die Schaltfläche Tools.
- Klicken Sie auf die Schaltfläche Console.
- Führen Sie in der Console die folgenden Befehle aus, um zum Verzeichnis
public
zu wechseln und alle zugehörigen Dateien anzusehen:
cd public
ls
Die mit gzip komprimierte Version des Bundles, main.bundle.js.gz
, wird jetzt hier gespeichert. CompressionPlugin
komprimiert index.html
außerdem standardmäßig.
Als Nächstes muss der Server angewiesen werden, diese mit gzip komprimierten Dateien zu senden, wenn ihre ursprünglichen JS-Versionen angefordert werden. Dazu kann in server.js
eine neue Route definiert werden, bevor die Dateien mit express.static
bereitgestellt werden.
const express = require('express'); const app = express(); app.get('*.js', (req, res, next) => { req.url = req.url + '.gz'; res.set('Content-Encoding', 'gzip'); next(); }); app.use(express.static('public')); //...
Mit app.get
wird dem Server mitgeteilt, wie er auf eine GET-Anfrage für einen bestimmten Endpunkt antworten soll. Mit einer Callback-Funktion wird dann definiert, wie diese Anfrage verarbeitet werden soll. Die Route funktioniert so:
- Wenn Sie
'*.js'
als erstes Argument angeben, funktioniert dies für jeden Endpunkt, der ausgelöst wird, um eine JS-Datei abzurufen. - Im Callback wird
.gz
an die URL der Anfrage angehängt und der AntwortheaderContent-Encoding
ist aufgzip
gesetzt. - Schließlich sorgt
next()
dafür, dass die Sequenz zu jedem nachfolgenden Callback fortgeführt wird.
Sehen Sie sich nach dem Aktualisieren der App noch einmal den Bereich Network
an.
Wie zuvor wurde die Paketgröße deutlich reduziert.
Fazit
In diesem Codelab wurde das Komprimieren und Komprimieren von Quellcode behandelt. Beide Verfahren werden in vielen der heute verfügbaren Tools zum Standard. Daher ist es wichtig herauszufinden, ob Ihre Toolchain sie bereits unterstützt oder ob Sie beide Prozesse selbst anwenden sollten.