Netzwerknutzlasten mit gzip reduzieren und komprimieren

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.

App – Screenshot

Messen

Bevor Sie Optimierungen vornehmen, sollten Sie immer zuerst den aktuellen Status der Anwendung analysieren.

  • Um die Website als Vorschau anzusehen, wählen Sie App ansehen und dann Vollbild Vollbild aus.

Mit dieser App, die auch im Codelab Ungenutzten Code entfernen behandelt wurde, können Sie für Ihr Lieblingskätzchen abstimmen. 🐈

Sehen wir uns nun an, wie groß diese Anwendung ist:

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

Ursprüngliche Bundle-Größe im Netzwerkbereich

Obwohl im Codelab „Nicht verwendeten Code entfernen“ große Fortschritte gemacht wurden, um die Größe des Bundles zu reduzieren, sind 225 KB immer noch recht groß.

Reduzierung

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 wurden, sieht der Code so aus:

function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}

Die Datei wäre dann bei etwa 83 B groß. Wenn 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. Die JavaScript-Engine des Browsers interpretiert jedoch alle auf die gleiche Weise. Durch diese Verschleierung von Code lassen sich kleinere Dateigrößen erzielen. 112 B waren am Anfang eigentlich nicht viel, aber die Größe wurde 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. Dabei wird TerserWebpackPlugin, ein Plug-in für Terser, verwendet. Terser ist ein beliebtes Tool zum Komprimieren von JavaScript-Code.

Um eine Vorstellung davon zu bekommen, wie der reduzierte Code aussieht, klicken Sie im Bereich Netzwerk der Entwicklertools auf main.bundle.js. Klicken Sie jetzt auf den Tab Response (Antwort).

Minimale Antwort

Der Code wird in seiner endgültigen Form, reduziert und manipuliert, im Antworttext angezeigt. Wenn Sie herausfinden möchten, wie groß das Bundle sein könnte, wenn es nicht minimiert wurde, öffnen Sie webpack.config.js und aktualisieren Sie die Konfiguration mode.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

Aktualisieren Sie die Anwendung und sehen Sie sich die Bundle-Größe im Bereich Netzwerk der Entwicklertools noch einmal an.

Bundle-Größe von 767 KB

Das ist ein großer Unterschied! 😅

Setzen Sie die Änderungen hier auf jeden Fall zurück, bevor Sie fortfahren.

module.exports = {
  mode: 'production',
  mode: 'none',
  //...

Das Einbinden eines Prozesses zum Komprimieren von Code in Ihrer Anwendung hängt von den verwendeten Tools ab:

  • Bei Verwendung von Webpack Version 4 oder höher ist keine zusätzliche Arbeit erforderlich, da der Code im Produktionsmodus standardmäßig reduziert wird. 👍
  • Wenn eine ältere Webpack-Version verwendet wird, installieren Sie TerserWebpackPlugin und nehmen Sie in den Webpack-Build-Prozess auf. In der Dokumentation wird dies im Detail erläutert.
  • Es gibt auch andere Reduzierungs-Plug-ins, z. B. BabelMinifyWebpackPlugin und ClosureCompilerPlugin.
  • Wenn ein Modul-Bundler überhaupt nicht verwendet wird, verwenden Sie Terser als CLI-Tool oder fügen Sie es direkt als Abhängigkeit ein.

Komprimierung

Obwohl der Begriff "Komprimierung" manchmal locker verwendet wird, um zu erklären, wie Code bei der Reduzierung von Code reduziert wird, wird er nicht im wörtlichen Sinn komprimiert.

Komprimierung bezieht sich in der Regel auf Code, der mithilfe eines Datenkomprimierungsalgorithmus geändert wurde. Im Gegensatz zur Komprimierung, die letztendlich perfekt gültigen Code liefert, muss komprimierter Code vor der Verwendung dekomprimiert werden.

Mit jeder HTTP-Anfrage und -Antwort können Browser und Webserver headers mit zusätzlichen Informationen über das abgerufene oder empfangene Asset hinzufügen. Du findest das im Bereich „Netzwerk“ der Entwicklertools auf dem Tab Headers. Dort sind drei Typen zu sehen:

  • Allgemein steht für allgemeine Header, die für die gesamte Anfrage-Antwort-Interaktion relevant sind.
  • Antwortheader zeigt eine Liste von Headern an, die für die tatsächliche Antwort vom Server spezifisch sind.
  • Anfrageheader zeigt eine Liste von Headern, die vom Client an die Anfrage angehängt wurden.

Sehen Sie sich den accept-encoding-Header im Request Headers an.

Codierungsheader akzeptieren

accept-encoding wird vom Browser verwendet, um anzugeben, welche Inhaltscodierungsformate oder Komprimierungsalgorithmen unterstützt werden. Es gibt viele Algorithmen zur Textkomprimierung, aber nur drei davon werden für die Komprimierung (und Dekomprimierung) von HTTP-Netzwerkanfragen unterstützt:

  • Gzip (gzip): Das am häufigsten verwendete Komprimierungsformat für Server- und Clientinteraktionen. Sie baut auf dem Deflate-Algorithmus auf und wird von allen aktuellen Browsern unterstützt.
  • Verkleinern (deflate): Nicht häufig verwendet.
  • Brotli (br): Ein neuerer Komprimierungsalgorithmus, der darauf abzielt, die Komprimierungsverhältnisse zu verbessern, was zu einem noch schnelleren Laden der Seiten führen kann. Es wird in den aktuellen Versionen der meisten Browser unterstützt.

Die Beispielanwendung in dieser Anleitung ist mit der Anwendung im Codelab Ungenutzten 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 spontan komprimiert, wenn sie vom Browser angefordert werden.

Vorteile

  • Das Erstellen und Aktualisieren gespeicherter komprimierter Versionen von Assets muss nicht ausgeführt werden.
  • Die spontane Komprimierung funktioniert besonders gut bei dynamisch generierten Webseiten.

Nachteile

  • Die Komprimierung von Dateien auf höheren Ebenen für bessere Komprimierungsverhältnisse dauert länger. 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);
});

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

Um sicherzustellen, dass alle Assets bei jeder Anforderung komprimiert werden, kann die Kompressions-Middleware-Bibliothek verwendet werden. Fügen Sie ihn zuerst als devDependency in package.json hinzu:

"devDependencies": {
  //...
  "compression": "^1.7.3"
},

Importieren Sie sie 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 die Bundle-Größe im Bereich Netzwerk an.

Bundle-Größe mit dynamischer Komprimierung

Von 225 KB auf 61,6 KB! In Response Headers zeigt ein content-encoding-Header jetzt an, dass der Server diese mit gzip codierte Datei nach unten sendet.

Header für Inhaltscodierung

Statische Komprimierung

Der Grundgedanke der statischen Komprimierung besteht darin, dass Assets komprimiert und vorab gespart werden.

Vorteile

  • Die Latenz aufgrund hoher Komprimierungsgrade ist kein Problem mehr. Dateien müssen nicht spontan komprimiert werden, da sie jetzt direkt abgerufen werden können.

Nachteile

  • Assets müssen mit jedem Build komprimiert werden. Die Build-Zeiten können bei hohen Komprimierungsstufen erheblich länger werden.

Statische Komprimierung mit Node/Express und Webpack

Da bei der statischen Komprimierung Dateien vorab komprimiert werden, können die Webpack-Einstellungen geändert werden, um Assets im Rahmen des Build-Schritts zu komprimieren. Dafür kann CompressionPlugin verwendet werden.

Fügen Sie ihn 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 ihn 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. Öffnen Sie die Glitch-Konsole und sehen Sie sich an, was sich im letzten public/-Verzeichnis befindet, 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 zu sehen:
cd public
ls

Endgültig ausgegebene Dateien im öffentlichen Verzeichnis

Die mit gzip komprimierte Version des Bundles main.bundle.js.gz ist jetzt auch hier gespeichert. CompressionPlugin komprimiert index.html auch standardmäßig.

Als Nächstes muss der Server angewiesen werden, diese gzip-Dateien jedes Mal zu senden, wenn ihre ursprünglichen JS-Versionen angefordert werden. Definieren Sie dazu eine neue Route in server.js, 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'));

//...

app.get wird verwendet, um dem Server mitzuteilen, wie er auf eine GET-Anfrage für einen bestimmten Endpunkt antworten soll. Mithilfe 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 zum Abrufen einer JS-Datei ausgelöst wird.
  • Innerhalb des Callbacks ist .gz an die URL der Anfrage angehängt und der Antwortheader Content-Encoding auf gzip gesetzt.
  • Schließlich sorgt next() dafür, dass die Sequenz mit jedem Callback fortgesetzt wird, der möglicherweise als Nächstes kommt.

Rufe das Steuerfeld „Network“ nach dem Aktualisieren der App noch einmal auf.

Reduzierung der Bundle-Größe mit statischer Komprimierung

Wie zuvor eine deutliche Reduzierung der Set-Größe!

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 selbst damit beginnen sollten, beide Prozesse anzuwenden.