Netzwerknutzlasten mit gzip reduzieren und komprimieren

In diesem Codelab wird untersucht, wie sich die Minimierung und Komprimierung des JavaScript-Bundles für die folgende Anwendung auf die Seitenleistung auswirkt, indem die Anfragegröße der App reduziert wird.

App – Screenshot

Messen

Bevor Sie mit der Optimierung beginnen, sollten Sie den aktuellen Zustand der Anwendung analysieren.

  • Wenn Sie sich eine Vorschau der Website ansehen möchten, drücken Sie App ansehen und dann Vollbild 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:

  1. Drücken Sie „Strg + Umschalttaste + J“ (oder „Befehlstaste + Optionstaste + J“ auf einem Mac), um die Entwicklertools zu öffnen.
  2. Klicken Sie auf den Tab Netzwerk.
  3. Klicken Sie das Kästchen Cache deaktivieren an.
  4. Laden Sie die App neu.

Ursprüngliche Bundle-Größe im Netzwerkbereich

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 so 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 beträgt jetzt 62 B.

Mit jedem Schritt wird der Code schwerer zu lesen. Die JavaScript-Engine des Browsers interpretiert sie jedoch genau gleich. 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",
 
//...
}

In Version 4 wird das Bundle im Produktionsmodus bereits standardmäßig minimiert. Es verwendet TerserWebpackPluginein Plug-in für Terser. Terser ist ein beliebtes Tool zum Komprimieren von JavaScript-Code.

Wenn Sie sich ein Bild davon machen möchten, wie der minimierte Code aussieht, klicken Sie im Bereich Netzwerk der Entwicklertools auf main.bundle.js. Klicken Sie jetzt auf den Tab Antwort.

Minimierte Antwort

Der Code in seiner endgültigen Form, minimiert und unlesbar, wird 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.

Bundle-Größe von 767 KB

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:

  • Wenn webpack v4 oder höher verwendet wird, sind keine zusätzlichen Schritte erforderlich, da der Code im Produktionsmodus standardmäßig minimiert 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:

  • Allgemein 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-Header

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 von 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 werden sowohl die statische als auch die dynamische Komprimierung behandelt.

Dynamische Komprimierung

Bei der dynamischen Komprimierung werden Assets direkt komprimiert, wenn sie vom Browser angefordert werden.

Vorteile

  • Es ist nicht erforderlich, gespeicherte komprimierte Versionen von Assets zu erstellen und zu aktualisieren.
  • Das On-the-fly-Komprimieren 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 Node-Servers 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);
});

Derzeit wird nur express importiert und die express.static-Middleware verwendet, um alle statischen HTML-, JS- und CSS-Dateien im Verzeichnis public/ zu laden. Diese Dateien werden bei jedem Build von webpack erstellt.

Damit alle Assets jedes Mal komprimiert werden, wenn sie angefordert werden, kann die Middleware-Bibliothek für die 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'));

//...

Laden Sie die App jetzt neu und sehen Sie sich die Bundle-Größe im Bereich Netzwerk an.

Bundle-Größe mit dynamischer Komprimierung

Von 225 KB auf 61,6 KB! Im Response Headers ist jetzt ein content-encoding-Header zu sehen, der angibt, dass der Server diese Datei mit gzip codiert sendet.

Content-Encoding-Header

Statische Komprimierung

Bei der statischen Komprimierung werden Assets vorab komprimiert und gespeichert.

Vorteile

  • Latenzen aufgrund hoher Komprimierungsstufen sind kein Problem 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 Konsole die folgenden Befehle aus, um in das Verzeichnis public zu wechseln und alle darin enthaltenen Dateien aufzurufen:
cd public
ls

Endgültige Ausgabedateien im öffentlichen Verzeichnis

Die gezippte Version des Bundles, main.bundle.js.gz, wird jetzt auch hier gespeichert. CompressionPlugin komprimiert standardmäßig auch index.html.

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 der Content-Encoding-Antwortheader auf gzip 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.

Bundle-Größe durch statische Komprimierung reduzieren

Wie bereits erwähnt, wurde die Größe des Bundles deutlich reduziert.

Fazit

In diesem Codelab ging es um das Minimieren und Komprimieren von Quellcode. 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.