In diesem Codelab wird gezeigt, 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, drücken Sie App ansehen und dann Vollbild .
In dieser App, die auch im Codelab Nicht verwendeten Code entfernen behandelt 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 + Optionstaste + J auf einem Mac), um die Entwicklertools zu öffnen.
- Klicken Sie auf den Tab Netzwerk.
- Klicken Sie das Kästchen Cache deaktivieren an.
- Laden Sie die App neu.
Im Codelab Nicht verwendeten Code entfernen wurden große Fortschritte erzielt, um die Größe des Bundles zu reduzieren. 225 KB sind aber immer noch ziemlich viel.
Minimierung
Sehen Sie sich den folgenden Codeblock an.
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 Code wie folgt aus:
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
Die Dateigröße würde jetzt etwa 83 B betragen. Wenn der Code weiter beschädigt wird, indem die Länge des Variablennamens reduziert und einige Ausdrücke geändert werden, könnte 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 schwerer zu lesen. Das JavaScript-Modul des Browsers interpretiert diese jedoch jeweils auf die gleiche Weise. Der Vorteil dieser Verschleierung von Code besteht darin, dass kleinere Dateigrößen erreicht werden können. 112 B war zwar schon von vornherein nicht viel, aber die Größe wurde trotzdem um 50 % reduziert.
In dieser Anwendung wird webpack Version 4 als Modul-Bundler verwendet. Die spezifische Version finden Sie unter package.json
.
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
Version 4 reduziert das Bundle bereits standardmäßig im Produktionsmodus. Es verwendet TerserWebpackPlugin
ein Plug-in für Terser.
Terser ist ein beliebtes Tool zur Komprimierung von JavaScript-Code.
Wenn Sie eine Vorstellung davon erhalten möchten, wie der komprimierte Code aussieht, klicken Sie im Bereich Netzwerk in den Entwicklertools auf main.bundle.js
. Klicken Sie jetzt auf den Tab Antwort.
Der Code wird in seiner endgültigen Form, reduziert und manipuliert, im Antworttext angezeigt.
Wenn Sie herausfinden möchten, wie groß das Bundle ohne Minimierung gewesen wäre, öffnen Sie webpack.config.js
und aktualisieren Sie die mode
-Konfiguration.
module.exports = {
mode: 'production',
mode: 'none',
//...
Laden Sie die Anwendung neu 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 Minimieren von Code in Ihre 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 von webpack verwendet wird, installieren Sie
TerserWebpackPlugin
und schließen Sie sie in den webpack-Buildprozess ein. In der Dokumentation wird dies ausführlich erläutert. - Es gibt auch andere Minimierungs-Plug-ins, die stattdessen verwendet werden können, z. B. BabelMinifyWebpackPlugin und ClosureCompilerPlugin.
- Wenn kein Modul-Bundler verwendet wird, verwenden Sie Terser als Befehlszeilentool oder fügen Sie es direkt als Abhängigkeit hinzu.
Komprimierung
Der Begriff „Komprimierung“ wird manchmal locker verwendet, um zu erklären, wie Code während des Minimierungsvorgangs reduziert wird. Er wird jedoch nicht im wörtlichen Sinne komprimiert.
Komprimierung bezieht sich in der Regel auf Code, der mit einem Datenkomprimierungsalgorithmus geändert wurde. Im Gegensatz zur Minimierung, bei der ein fehlerfreier Code entsteht, muss komprimierter Code vor der Verwendung dekomprimiert werden.
Browser und Webserver können jeder HTTP-Anfrage und ‑Antwort Header hinzufügen, um zusätzliche Informationen zum abgerufenen oder empfangenen Asset anzugeben. Das ist auf dem Tab Headers
im DevTools-Bereich „Netzwerk“ zu sehen, wo drei Typen angezeigt werden:
- General steht für allgemeine Header, die für die gesamte Anfrage-Antwort-Interaktion relevant sind.
- Unter Antwortheader sehen Sie eine Liste der Header, die für die tatsächliche Antwort vom Server spezifisch sind.
- Unter Anfrageheader sehen Sie eine Liste der Header, die vom Client an die Anfrage angehängt wurden.
Sehen Sie sich den accept-encoding
-Header in der Request Headers
an.
accept-encoding
wird vom Browser verwendet, um anzugeben, welche Inhaltscodierungsformate oder Komprimierungsalgorithmen er unterstützt. Es gibt viele Textkomprimierungsalgorithmen, aber nur drei werden hier für die Komprimierung (und Dekomprimierung) von HTTP-Netzwerkanfragen unterstützt:
- Gzip (
gzip
): Das gängigste Komprimierungsformat für Server- und Clientinteraktionen. Er basiert auf dem Deflate-Algorithmus und wird in allen aktuellen Browsern unterstützt. - Deflate (
deflate
): Wird nicht häufig verwendet. - Brotli (
br
): Ein neuerer Komprimierungsalgorithmus, der das Komprimierungsverhältnis weiter verbessern soll, was zu einer noch schnelleren Seitenladezeit führen kann. Es wird in den aktuellen Versionen der meisten Browser unterstützt.
Die Beispielanwendung in diesem Tutorial ist mit der App identisch, die im Codelab Nicht verwendeten Code entfernen erstellt wurde, mit der Ausnahme, 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 unmittelbar dann komprimiert, wenn sie vom Browser angefordert werden.
Vorteile
- Es ist nicht erforderlich, gespeicherte komprimierte Versionen von Assets zu erstellen und zu aktualisieren.
- Die On-the-fly-Komprimierung eignet sich besonders gut für Webseiten, die dynamisch generiert werden.
Nachteile
- Das Komprimieren von Dateien auf höheren Ebenen, um bessere Komprimierungsraten zu erzielen, dauert länger. Das kann zu Leistungseinbußen führen, da der Nutzer warten muss, bis die Assets komprimiert wurden, 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"
},
und importieren Sie sie in die Serverdatei server.js
:
const express = require('express');
const compression = require('compression');
Fügen Sie es als Middleware vor dem Bereitstellen von express.static
hinzu:
//...
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. Dateien müssen nicht mehr on-the-fly komprimiert werden, da sie jetzt direkt abgerufen werden können.
Nachteile
- Assets müssen bei jedem Build komprimiert werden. Die Build-Zeiten können sich erheblich verlängern, wenn hohe Komprimierungsstufen verwendet werden.
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 dafür 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");
und fügen Sie sie in das plugins
-Array ein:
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
Standardmäßig komprimiert das Plug-in die Build-Dateien 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. Öffnen Sie die Glitch Console, um sich anzusehen, was sich im endgültigen public/
-Verzeichnis befindet, das vom Node-Server 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 müssen Sie dem Server mitteilen, dass er diese komprimierten Dateien senden soll, wenn die ursprünglichen JS-Versionen angefordert werden. Dazu definieren Sie in server.js
eine neue Route, 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 reagieren soll. Mit einer Callback-Funktion wird dann festgelegt, wie diese Anfrage verarbeitet werden soll. Die Route funktioniert so:
- Wenn du
'*.js'
als erstes Argument angibst, funktioniert das für jeden Endpunkt, der zum Abrufen einer JS-Datei ausgelöst wird. - Im Callback wird
.gz
an die URL der Anfrage angehängt und derContent-Encoding
-Antwortheader aufgzip
gesetzt. - Schließlich sorgt
next()
dafür, dass die Sequenz mit dem nächsten möglichen Rückruf fortgesetzt wird.
Nachdem die App neu geladen wurde, sieh dir das Steuerfeld Network
noch einmal an.
Wie zuvor wurde die Paketgröße deutlich reduziert.
Fazit
In diesem Codelab wurde das Komprimieren und Komprimieren von Quellcode behandelt. Beide Techniken werden in vielen der heute verfügbaren Tools standardmäßig unterstützt. Daher ist es wichtig, herauszufinden, ob Ihre Toolchain sie bereits unterstützt oder ob Sie beide Prozesse selbst anwenden sollten.