So unterstützt Webpack das Asset-Caching
Als Nächstes (nach der Optimierung der App-Größe um die Ladezeit der App im Cache zu verbessern. Hiermit können Sie dafür sorgen, dass Teile der App und nicht jedes Mal wieder herunterladen.
Bundle-Versionsverwaltung und Cache-Header verwenden
Der gängige Ansatz beim Caching ist:
den Browser anweisen, eine Datei sehr lange (z.B. ein Jahr) im Cache zu speichern:
# Server header Cache-Control: max-age=31536000
Wenn Sie nicht wissen, was
Cache-Control
tut, sehen Sie sich die hervorragender Post zum Caching .und benennen Sie die Datei nach der Änderung um, um den erneuten Download zu erzwingen:
<!-- Before the change --> <script src="./index-v15.js"></script> <!-- After the change --> <script src="./index-v16.js"></script>
Dadurch wird der Browser angewiesen, die JS-Datei herunterzuladen, im Cache zu speichern und im Cache gespeicherter Kopie. Der Browser trifft nur dann auf das Netzwerk, wenn sich der Dateiname ändert (oder nach Ablauf eines Jahres).
Mit Webpack tun Sie dasselbe, aber Sie geben anstelle einer Versionsnummer die
Datei-Hash. Um den Hash in den Dateinamen aufzunehmen, verwenden Sie
[chunkhash]
:
// webpack.config.js
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.[chunkhash].js' // → bundle.8e0d62a03.js
}
};
Wenn Sie die
Dateinamen, um ihn an den Client zu senden, entweder HtmlWebpackPlugin
oder den
WebpackManifestPlugin
.
Das HtmlWebpackPlugin
ist ein
aber weniger flexibel ist. Während der Kompilierung generiert dieses Plug-in ein
HTML-Datei, die alle kompilierten Ressourcen enthält. Wenn Ihre Serverlogik nicht
ist, sollte es für Sie ausreichen:
<!-- index.html -->
<!DOCTYPE html>
<!-- ... -->
<script src="bundle.8e0d62a03.js"></script>
Die
WebpackManifestPlugin
ist ein flexiblerer Ansatz, der bei einer komplexen Serverkomponente nützlich ist.
Während des Builds wird eine JSON-Datei mit einer Zuordnung zwischen den Dateinamen generiert.
ohne Hash und Dateinamen mit Hash. Verwenden Sie diesen JSON-Code auf dem Server, um
mit welcher Datei Sie arbeiten möchten:
// manifest.json
{
"bundle.js": "bundle.8e0d62a03.js"
}
Weitere Informationen
- Jake Archibald über Caching am besten Best Practices
Abhängigkeiten und Laufzeit in eine separate Datei extrahieren
Abhängigkeiten
App-Abhängigkeiten ändern sich tendenziell seltener als der tatsächliche App-Code. Wenn du umziehst in einer separaten Datei speichern, kann der Browser sie separat im Cache speichern. Sie werden nicht jedes Mal neu heruntergeladen, wenn sich der App-Code ändert.
Führen Sie drei Schritte aus, um Abhängigkeiten in einen separaten Block zu extrahieren:
Ersetzen Sie den Namen der Ausgabedatei durch
[name].[chunkname].js
:// webpack.config.js module.exports = { output: { // Before filename: 'bundle.[chunkhash].js', // After filename: '[name].[chunkhash].js' } };
Wenn die App mit Webpack erstellt wird, ersetzt es
[name]
mit dem Namen eines Chunks. Ohne[name]
-Teil erhalten wir Blöcke anhand ihrer Hashwerte zu unterscheiden. Das ist ziemlich schwierig!Konvertieren Sie das Feld
entry
in ein Objekt:// webpack.config.js module.exports = { // Before entry: './index.js', // After entry: { main: './index.js' } };
In diesem Snippet ist der Name eines Chunks. Dieser Name wird in anstelle von
[name]
aus Schritt 1.Wenn Sie die App erstellt haben, enthält dieser Block mittlerweile den gesamten App-Code. wie wir diese Schritte nicht gemacht haben. Aber das ändert sich in einer Sekunde.
Füge in Webpack 4 die Option
optimization.splitChunks.chunks: 'all'
hinzu in Ihre Webpack-Konfiguration einfügen:// webpack.config.js (for webpack 4) module.exports = { optimization: { splitChunks: { chunks: 'all' } } };
Mit dieser Option wird die intelligente Codeaufteilung aktiviert. Damit würde Webpack den Anbietercode extrahieren, wird sie größer als 30 KB (vor der Reduzierung und GZIP). Außerdem würde der Standardcode extrahiert werden, Dies ist nützlich, wenn Ihr Build mehrere Bundles (z.B. wenn Sie Ihre App in Routen aufteilen.
Füge in Webpack 3 das
CommonsChunkPlugin
hinzu:// webpack.config.js (for webpack 3) module.exports = { plugins: [ new webpack.optimize.CommonsChunkPlugin({ // A name of the chunk that will include the dependencies. // This name is substituted in place of [name] from step 1 name: 'vendor', // A function that determines which modules to include into this chunk minChunks: module => module.context && module.context.includes('node_modules'), }) ] };
Dieses Plug-in verwendet alle Module, bei denen Pfade
node_modules
und werden sie in eine separate Datei namensvendor.[chunkhash].js
verschoben.
Nach diesen Änderungen generiert jeder Build zwei Dateien statt einer: main.[chunkhash].js
und
vendor.[chunkhash].js
(vendors~main.[chunkhash].js
für Webpack 4). Bei Webpack 4
Bei kleinen Abhängigkeiten wird das Anbieterpaket möglicherweise nicht generiert. Das ist in Ordnung:
$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
Asset Size Chunks Chunk Names
./main.00bab6fd3100008a42b0.js 82 kB 0 [emitted] main
./vendor.d9e134771799ecdf9483.js 47 kB 1 [emitted] vendor
Der Browser speichert diese Dateien separat im Cache und lädt nur Code noch einmal herunter, der sich ändert.
Webpack-Laufzeitcode
Leider reicht es nicht aus, nur den Anbietercode zu extrahieren. Wenn Sie versuchen, App-Code ändern:
// index.js
…
…
// E.g. add this:
console.log('Wat');
werden Sie feststellen, dass sich auch der Hash vendor
ändert:
Asset Size Chunks Chunk Names
./vendor.d9e134771799ecdf9483.js 47 kB 1 [emitted] vendor
↓
Asset Size Chunks Chunk Names
./vendor.e6ea4504d61a1cc1c60b.js 47 kB 1 [emitted] vendor
Dies liegt daran, dass das Webpack-Bundle neben dem Code der Module über einen Runtime – ein kleines Code-Snippet der die Modulausführung verwaltet. Wenn Sie den Code in mehrere Dateien aufteilen, beginnt dieser Code mit einer Zuordnung zwischen den Block-IDs und entsprechenden Dateien:
// vendor.e6ea4504d61a1cc1c60b.js
script.src = __webpack_require__.p + chunkId + "." + {
"0": "2f2269c7f0a55a5c1871"
}[chunkId] + ".js";
Webpack fügt diese Laufzeit in den letzten generierten Block ein, nämlich vendor
in unserem Fall. Jedes Mal, wenn Chunk-Änderungen
geändert werden, ändert sich auch dieses Code-Snippet,
Dadurch ändert sich der gesamte Block vendor
.
Um dieses Problem zu lösen, verschieben wir die Laufzeit in eine separate Datei. In Webpack 4:
durch Aktivieren der Option optimization.runtimeChunk
erreicht:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
runtimeChunk: true
}
};
Erstellen Sie dazu in Webpack 3 einen zusätzlichen leeren Block mit dem CommonsChunkPlugin
:
// webpack.config.js (for webpack 3)
module.exports = {
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: module => module.context && module.context.includes('node_modules')
}),
// This plugin must come after the vendor one (because webpack
// includes runtime into the last chunk)
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
// minChunks: Infinity means that no app modules
// will be included into this chunk
minChunks: Infinity
})
]
};
Nach diesen Änderungen generiert jeder Build drei Dateien:
$ webpack
Hash: ac01483e8fec1fa70676
Version: webpack 3.8.1
Time: 3816ms
Asset Size Chunks Chunk Names
./main.00bab6fd3100008a42b0.js 82 kB 0 [emitted] main
./vendor.26886caf15818fa82dfa.js 46 kB 1 [emitted] vendor
./runtime.79f17c27b335abc7aaf4.js 1.45 kB 3 [emitted] runtime
Fügen Sie sie in umgekehrter Reihenfolge unter index.html
ein. Damit sind Sie fertig:
<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>
<script src="./vendor.26886caf15818fa82dfa.js"></script>
<script src="./main.00bab6fd3100008a42b0.js"></script>
Weitere Informationen
- Webpack-Leitfaden zum langfristigen Caching
- Webpack-Dokumentation über die Webpack-Laufzeit und Manifest
- „Sie können den CommonsChunkPlugin"
- Funktionsweise von
optimization.splitChunks
undoptimization.runtimeChunk
Inline-Webpack-Laufzeit zum Speichern einer zusätzlichen HTTP-Anfrage
Zur Verbesserung kannst du die Webpack-Laufzeit in den HTML-Code aufnehmen. Antwort. Anstelle von:
<!-- index.html -->
<script src="./runtime.79f17c27b335abc7aaf4.js"></script>
Gehen Sie wie folgt vor:
<!-- index.html -->
<script>
!function(e){function n(r){if(t[r])return t[r].exports;…}} ([]);
</script>
Da die Laufzeit klein ist, können Sie HTTP-Anfragen speichern, wichtig bei HTTP/1; bei HTTP/2 weniger wichtig ist, aber trotzdem Effekt).
Und so geht's!
Wenn Sie HTML mit dem HTMLWebpackPlugin generieren
Wenn Sie die Methode HtmlWebpackPlugin zum Generieren der eine HTML-Datei enthält, InlineSourcePlugin ist alles, was Sie brauchen:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const InlineSourcePlugin = require('html-webpack-inline-source-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
inlineSource: 'runtime~.+\\.js',
}),
new InlineSourcePlugin()
]
};
Wenn Sie HTML mithilfe einer benutzerdefinierten Serverlogik generieren
Mit Webpack 4:
Fügen Sie den
WebpackManifestPlugin
finden Sie den generierten Namen des Laufzeit-Chunks:// webpack.config.js (for webpack 4) const ManifestPlugin = require('webpack-manifest-plugin'); module.exports = { plugins: [ new ManifestPlugin() ] };
Ein Build mit diesem Plug-in würde eine Datei erstellen, die so aussieht:
// manifest.json { "runtime~main.js": "runtime~main.8e0d62a03.js" }
Fügen Sie den Inhalt des Laufzeit-Chunks auf eine bequeme Weise ein. Beispiel: mit Node.js und Express:
// server.js const fs = require('fs'); const manifest = require('./manifest.json'); const runtimeContent = fs.readFileSync(manifest['runtime~main.js'], 'utf-8'); app.get('/', (req, res) => { res.send(` … <script>${runtimeContent}</script> … `); });
Oder mit Webpack 3:
Machen Sie den Laufzeitnamen statisch, indem Sie
filename
angeben:module.exports = { plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'runtime', minChunks: Infinity, filename: 'runtime.js' }) ] };
Fügen Sie den
runtime.js
-Inhalt auf eine bequeme Weise ein. Beispiel: mit Node.js und Express:// server.js const fs = require('fs'); const runtimeContent = fs.readFileSync('./runtime.js', 'utf-8'); app.get('/', (req, res) => { res.send(` … <script>${runtimeContent}</script> … `); });
Lazy-Loading-Code, den Sie gerade nicht benötigen
Manchmal besteht eine Seite aus mehr und weniger wichtigen Teilen:
- Wenn Sie eine Videoseite auf YouTube laden, ist das Video für Sie wichtiger als das Video. Kommentare. Hier ist das Video wichtiger als Kommentare.
- Wenn Sie einen Artikel auf einer Nachrichten-Website öffnen, ist Ihnen der Text des als über Anzeigen. Hier ist der Text wichtiger als die Anzeigen.
Verbessern Sie in diesen Fällen die anfängliche Ladeleistung, indem Sie nur die
die wichtigsten Elemente zuerst und
das Lazy Loading für die verbleibenden Teile später. Verwenden Sie das
import()
-Funktion und
code-splitting dafür:
// videoPlayer.js
export function renderVideoPlayer() { … }
// comments.js
export function renderComments() { … }
// index.js
import {renderVideoPlayer} from './videoPlayer';
renderVideoPlayer();
// …Custom event listener
onShowCommentsClick(() => {
import('./comments').then((comments) => {
comments.renderComments();
});
});
import()
gibt an, dass ein bestimmtes Modul dynamisch geladen werden soll. Wann?
Webpack erkennt import('./module.js')
und verschiebt dieses Modul in ein
chunk:
$ webpack
Hash: 39b2a53cb4e73f0dc5b2
Version: webpack 3.8.1
Time: 4273ms
Asset Size Chunks Chunk Names
./0.8ecaf182f5c85b7a8199.js 22.5 kB 0 [emitted]
./main.f7e53d8e13e9a2745d6d.js 60 kB 1 [emitted] main
./vendor.4f14b6326a80f4752a98.js 46 kB 2 [emitted] vendor
./runtime.79f17c27b335abc7aaf4.js 1.45 kB 3 [emitted] runtime
und lädt sie erst herunter, wenn die Ausführung die Funktion import()
erreicht.
Dadurch wird das main
-Bundle kleiner, was die anfängliche Ladezeit verkürzt.
Umso mehr wird das Caching verbessert – wenn Sie den Code im Haupt-Chunk ändern,
Dies hat keinen Einfluss auf den Kommentar-Chunk.
Weitere Informationen
- Webpack-Dokumente für
import()
- Der JavaScript-Vorschlag zur Implementierung der
import()
Syntax
Code in Routen und Seiten aufteilen
Wenn Ihre App mehrere Routen oder Seiten umfasst, aber nur eine einzige JS-Datei mit
(ein einzelner main
-Chunk) haben, stellen Sie wahrscheinlich zusätzliche Bytes
für jede Anfrage. Angenommen, ein Nutzer besucht eine Startseite Ihrer Website:
müssen sie den Code zum Rendern eines Artikels nicht laden, Seite, aber sie wird geladen. Wenn ein Nutzer immer nur das Zuhause besucht, und Sie eine Änderung am Artikelcode vornehmen, wird das Webpack gesamtes Bundle, und der Nutzer muss die ganze App noch einmal herunterladen.
Wenn wir die App in Seiten (oder Routen, bei einer App mit nur einer Seite) aufteilen, wird nur der relevante Code heruntergeladen. Außerdem speichert der Browser den App-Code im Cache. besser: Wenn Sie den Startseitencode ändern, entwertet Webpack nur die entsprechenden Block aus.
Für Apps mit nur einer Seite
Um Single-Page-Anwendungen nach Routen aufzuteilen, verwenden Sie import()
(siehe Lazy-Loading-Code
die Sie im Moment nicht benötigen.“ Wenn Sie ein Framework verwenden,
könnte es bereits eine Lösung dafür geben:
- "Code
Aufteilung“
in der Dokumentation von
react-router
(für React) - „Lazy Loading
Routen“ in
Dokumente von
vue-router
(für Vue.js)
Für herkömmliche mehrseitige Apps
Um herkömmliche Apps nach Seiten aufzuteilen, verwenden Sie den Eintrag von Webpack. Punkte. Wenn Ihre App über drei z. B. die Startseite, die Artikelseite und die Kontoseite. sollte drei Einträge haben:
// webpack.config.js
module.exports = {
entry: {
home: './src/Home/index.js',
article: './src/Article/index.js',
profile: './src/Profile/index.js'
}
};
Für jede Eintragsdatei erstellt Webpack eine separate Abhängigkeitsstruktur und generiert Ein Bundle, das nur Module enthält, die von diesem Eintrag verwendet werden:
$ webpack
Hash: 318d7b8490a7382bf23b
Version: webpack 3.8.1
Time: 4273ms
Asset Size Chunks Chunk Names
./0.8ecaf182f5c85b7a8199.js 22.5 kB 0 [emitted]
./home.91b9ed27366fe7e33d6a.js 18 kB 1 [emitted] home
./article.87a128755b16ac3294fd.js 32 kB 2 [emitted] article
./profile.de945dc02685f6166781.js 24 kB 3 [emitted] profile
./vendor.4f14b6326a80f4752a98.js 46 kB 4 [emitted] vendor
./runtime.318d7b8490a7382bf23b.js 1.45 kB 5 [emitted] runtime
Wenn also nur auf der Artikelseite Lodash, die Sets home
und profile
verwendet werden
ist nicht enthalten – und der Nutzer muss diese Bibliothek nicht herunterladen,
auf die Startseite geleitet.
Separate Abhängigkeitsstrukturen haben jedoch auch ihre Nachteile. Wenn zwei Einstiegspunkte
und Sie haben Ihre Abhängigkeiten nicht
in ein Anbieter-Bundle verschoben,
Die Punkte enthalten eine Kopie von Lodash. Fügen Sie in Webpack 4 den
optimization.splitChunks.chunks: 'all'
in die Webpack-Konfiguration ein:
// webpack.config.js (for webpack 4)
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
Diese Option ermöglicht die intelligente Codeaufteilung. Mit dieser Option würde Webpack automatisch und extrahieren Sie ihn in separate Dateien.
Alternativ kannst du in Webpack 3 das CommonsChunkPlugin
verwenden.
– werden gängige Abhängigkeiten in eine neue angegebene Datei verschoben:
module.exports = {
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
minChunks: 2 // 2 is the default value
})
]
};
Sie können gerne mit dem Wert von minChunks
experimentieren, um die beste zu finden. Im Allgemeinen
sollten Sie sie klein halten, aber erhöhen, wenn die Anzahl der Blöcke zunimmt. Für
Bei drei Blöcken könnte minChunks
beispielsweise 2 sein, aber bei 30 Blöcken könnte es 8 sein.
Denn bei 2 laufen zu viele Module
in der gemeinsamen Datei,
zu viel aufblasen.
Weitere Informationen
- Webpack-Dokumente zum Konzept des Eintrags Punkte
- Webpack-Dokumentation über die CommonsChunkPlugin
- „Sie können den CommonsChunkPlugin"
- Funktionsweise von
optimization.splitChunks
undoptimization.runtimeChunk
Modul-IDs stabiler machen
Beim Erstellen des Codes weist Webpack jedem Modul eine ID zu. Später werden diese IDs
im Bundle in require()
Sekunden verwendet. Die Build-Ausgabe enthält normalerweise IDs
direkt vor den Modulpfaden ein:
$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
Asset Size Chunks Chunk Names
./0.8ecaf182f5c85b7a8199.js 22.5 kB 0 [emitted]
./main.4e50a16675574df6a9e9.js 60 kB 1 [emitted] main
./vendor.26886caf15818fa82dfa.js 46 kB 2 [emitted] vendor
./runtime.79f17c27b335abc7aaf4.js 1.45 kB 3 [emitted] runtime
↓ Hier
[0] ./index.js 29 kB {1} [built]
[2] (webpack)/buildin/global.js 488 bytes {2} [built]
[3] (webpack)/buildin/module.js 495 bytes {2} [built]
[4] ./comments.js 58 kB {0} [built]
[5] ./ads.js 74 kB {1} [built]
+ 1 hidden module
Standardmäßig werden IDs mit einem Zähler berechnet (d.h. das erste Modul hat die ID 0, hat die zweite die ID 1 usw. Das Problem dabei ist, dass Sie eines neuen Moduls erscheint, könnte es in der Mitte der Modulliste erscheinen und alle nächste Module IDs:
$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
Asset Size Chunks Chunk Names
./0.5c82c0f337fcb22672b5.js 22 kB 0 [emitted]
./main.0c8b617dfc40c2827ae3.js 82 kB 1 [emitted] main
./vendor.26886caf15818fa82dfa.js 46 kB 2 [emitted] vendor
./runtime.79f17c27b335abc7aaf4.js 1.45 kB 3 [emitted] runtime
[0] ./index.js 29 kB {1} [built]
[2] (webpack)/buildin/global.js 488 bytes {2} [built]
[3] (webpack)/buildin/module.js 495 bytes {2} [built]
↓ Wir haben ein neues Modul...
[4] ./webPlayer.js 24 kB {1} [built]
↓ Und sieh mal, wie das funktioniert! comments.js
hat jetzt die ID 5 statt 4
[5] ./comments.js 58 kB {0} [built]
↓ ads.js
hat jetzt ID 6 statt 5
[6] ./ads.js 74 kB {1} [built]
+ 1 hidden module
Dadurch werden alle Blöcke ungültig, die Module mit geänderten IDs enthalten oder von diesen abhängig sind.
auch wenn sich ihr
Code nicht geändert hat. In unserem Fall ist der Chunk 0
mit comments.js
) und der Block main
(der Teil mit dem anderen App-Code) erhalten
ungültig gemacht werden, während nur die main
hätte sein sollen.
Um dies zu beheben, ändern Sie die Berechnung von Modul-IDs mithilfe der
HashedModuleIdsPlugin
Sie ersetzt zählerbasierte IDs durch Hashes der Modulpfade:
$ webpack
Hash: df3474e4f76528e3bbc9
Version: webpack 3.8.1
Time: 2150ms
Asset Size Chunks Chunk Names
./0.6168aaac8461862eab7a.js 22.5 kB 0 [emitted]
./main.a2e49a279552980e3b91.js 60 kB 1 [emitted] main
./vendor.ff9f7ea865884e6a84c8.js 46 kB 2 [emitted] vendor
./runtime.25f5d0204e4f77fa57a1.js 1.45 kB 3 [emitted] runtime
↓ Hier
[3IRH] ./index.js 29 kB {1} [built]
[DuR2] (webpack)/buildin/global.js 488 bytes {2} [built]
[JkW7] (webpack)/buildin/module.js 495 bytes {2} [built]
[LbCc] ./webPlayer.js 24 kB {1} [built]
[lebJ] ./comments.js 58 kB {0} [built]
[02Tr] ./ads.js 74 kB {1} [built]
+ 1 hidden module
Bei diesem Ansatz ändert sich die ID eines Moduls nur, wenn Sie das Modul umbenennen oder verschieben -Modul. Neue Module haben keine Auswirkungen auf die IDs.
Fügen Sie das Plug-in zum Abschnitt plugins
der Konfiguration hinzu, um es zu aktivieren:
// webpack.config.js
module.exports = {
plugins: [
new webpack.HashedModuleIdsPlugin()
]
};
Weitere Informationen
- Webpack-Dokumentation über die HashedModuleIdsPlugin
Zusammenfassung
- Bundle im Cache speichern und durch Ändern des Bundle-Namens zwischen Versionen unterscheiden
- Paket in App-Code, Anbietercode und Laufzeit aufteilen
- Laufzeit zum Speichern einer HTTP-Anfrage inline einbinden
- Lazy Loading von nicht kritischem Code mit
import
- Code nach Routen/Seiten aufteilen, um unnötiges Laden zu vermeiden