Hintergrund
Es ist kein Geheimnis, dass sich mit der Veröffentlichung von Webpack 4 die Code-Sharing-Strategie stark verändert hat. Es ist sogar noch besser zu sagen, dass es wieder erfunden wurde, weil Der alte Ansatz hat einfach aufgehört zu funktionieren, und der neue ist nicht klar, wie man ihn verwendet.
Für diejenigen, die noch nicht auf dem neuesten Stand sind, ist das Plugin webpack.optimize.CommonsChunkPlugin weg. Absolut. Stattdessen wird empfohlen, Folgendes in die Konfiguration zu schreiben:
module.exports = {
Das sollte wie Magie funktionieren. Das heißt, Jetzt sagen wir Webpack nicht, was wir als gewöhnliches Stück tun sollen, aber er wird alles selbst machen und sogar besser als wir.
Und das Glück wird kommen. Ein Witz. Nicht wirklich ...
Grundvorbereitungen
Hier ist ein Beispiel aus der Dokumentation:
module.exports = { mode: 'development', entry: { index: './src/index.js', another: './src/another-module.js' }, output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') }, optimization: { splitChunks: { chunks: 'all' } } };
Das Ergebnis der Assembly sind 3 Dateien: another.bundle.js, index.bundle.js, vendor ~ another ~ index.bundle.js
Hash: ac2ac6042ebb4f20ee54 Version: webpack 4.7.0 Time: 316ms Asset Size Chunks Chunk Names another.bundle.js 5.95 KiB another [emitted] another index.bundle.js 5.89 KiB index [emitted] index vendors~another~index.bundle.js 547 KiB vendors~another~index [emitted] vendors~another~index Entrypoint index = vendors~another~index.bundle.js index.bundle.js Entrypoint another = vendors~another~index.bundle.js another.bundle.js [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 489 bytes {vendors~another~index} [built] [./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {vendors~another~index} [built] [./src/another-module.js] 88 bytes {another} [built] [./src/index.js] 86 bytes {index} [built] + 1 hidden module
Um unsere Webanwendungen zu starten, müssen wir in einem Fall die Anbieter ~ ein anderes ~ index.bundle.js und index.bundle.js und in den zweiten Anbietern ~ ein anderes ~ index.bundle.js und ein anderes verbinden. bundle.js.
Was ist das Problem?
Das Problem ist der Name Anbieter ~ ein anderer ~ Index .bundle.js. Solange wir weniger als drei Einstiegspunkte haben, passiert nichts Schlimmes. Hier scheint alles logisch - das Bundle enthält npm-Module (sie sind auch Anbieter) und allgemeine Module für den Index und andere. Auf jeder der Seiten enthalten wir 2 Dateien und haben keine Probleme.
Wenn wir jedoch drei oder mehr Einstiegspunkte haben, gibt es möglicherweise viel mehr neue Bundles (es handelt sich um Chunks), und wir kennen deren Nummer oder Namen nicht mehr. Alles macht noch mehr Spaß, wenn wir CSS auch in separate Dateien extrahieren. Und das ist das Problem.
Wie kann man dieses Problem lösen?
Nach Abschluss des Webpacks haben wir keine Dateien, die Informationen darüber enthalten, welche Bundles auf dieser oder jener Seite verbunden werden sollen. Und in welcher Reihenfolge.
In der Ausgabe finden wir jedoch folgende Zeilen:
Entrypoint index = vendors~another~index.bundle.js index.bundle.js Entrypoint another = vendors~another~index.bundle.js another.bundle.js
In der Tat ist dies fast das, was wir brauchen. Das heißt, webpack weiß sehr gut, welche Bundles für jeden Einstiegspunkt benötigt werden, möchte diese Informationen jedoch aus irgendeinem Grund nicht an uns weitergeben.
Das Manifest hilft uns hier nicht. Ja, wir wissen, dass es ein solches Bundle (Anbieter ~ ein anderes ~ index.bundle.js) gibt. Wir wissen, wo er liegt. Aber wir wissen nicht, wer es braucht. Das heißt, Das Manifest ist nutzlos.
Dann entschied ich, dass Webpack, da es die notwendigen Informationen kennt, möglicherweise über Plugins abgerufen werden kann. Ich fand keine fertigen und beschloss, meine eigenen zu schreiben. Und nur um dieses Plugin zu demonstrieren, schreibe ich diesen Artikel.
import * as webpack from "webpack"; export interface IChunkDescription { readonly id: string | number; readonly name: string; readonly files: string[]; } export interface IEntrypointsPluginOptions { readonly filename: string; readonly replacer?: (key: string, value: any) => any; readonly space?: string | number; readonly filter?: (chunk: IChunkDescription) => boolean; } export default class EntrypointsPlugin { private readonly options: IEntrypointsPluginOptions; public constructor(options: IEntrypointsPluginOptions) { this.options = Object.assign<IEntrypointsPluginOptions, IEntrypointsPluginOptions>({ filename: "entrypoints.json", replacer: null, space: null, filter: null }, options); } public apply(compiler: webpack.Compiler): void { compiler.hooks.emit.tap("entrypoints", (compilation: webpack.compilation.Compilation) => { let data = {}; let entrypoints = {}; const filter = this.options.filter; const publicPath = compilation.compiler.options.output.publicPath; for (let [key, value] of compilation.entrypoints.entries()) { const chunks: IChunkDescription[] = value.chunks.map(data => { const chunk: IChunkDescription = { id: data.id, name: data.name, files: data.files }; return filter == null || filter(chunk) ? chunk : null; }); const files = ([] as string[]).concat(...chunks.filter(c => c != null) .map(c => c.files.map(f => publicPath + f))); const js = files.filter(f => /.js/.test(f) && !/.js.map/.test(f)); const css = files.filter(f => /.css/.test(f) && !/.css.map/.test(f)); let entrypoint = {}; if (js.length) entrypoint["js"] = js; if (css.length) entrypoint["css"] = css; data[key] = entrypoint; } const json = JSON.stringify(data, this.options.replacer, this.options.space); compilation.assets[this.options.filename] = { source: () => json, size: () => json.length }; }); } }
Fügen Sie in der Datei webpack.config. (Ts | js) ein neues Plugin hinzu:
plugins: [ new EntrypointsPlugin({ filename: "entrypoints.json", space: 2 }) ]
und warten Sie auf das Ergebnis. Das Ergebnis ist eine entrypoints.json-Datei mit folgendem Inhalt:
{ "index": { "js": ["vendors~another~index.bundle.js", "index.bundle.js"] }, "another": { "js": ["vendors~another~index.bundle.js", "another.bundle.js"] } }
Wenn extract-css verwendet wird, gibt es neben dem Abschnitt js auch css.
Das Letzte, was uns beim Erstellen der HTML-Seite bleibt, ist, die Datei entrypoints.json zu lesen, den gewünschten Einstiegspunkt zu finden, js- und css-Dateien aus den entsprechenden Listen zu verbinden.
Das Problem ist gelöst
Irgendwie so.