Contexte
Ce n'est un secret pour personne qu'avec la sortie de Webpack 4, la stratégie de partage de code a beaucoup changé. C'est encore mieux de dire qu'il a été réinventé, car l'ancienne approche vient de cesser de fonctionner, et la nouvelle n'est pas claire comment l'utiliser.
Pour ceux qui ne sont toujours pas à jour, le plugin webpack.optimize.CommonsChunkPlugin a disparu. Absolument. Au lieu de cela, il est suggéré d'écrire ce qui suit dans la configuration:
module.exports = {
Cela devrait fonctionner comme par magie. C'est-à-dire Maintenant, nous ne disons pas à webpack quoi faire en tant que morceau commun, mais il fera tout lui-même, et encore mieux que nous.
Et le bonheur viendra. Une plaisanterie. Pas vraiment ...
Préparations de base
Voici un exemple tiré de la documentation:
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' } } };
Le résultat de l'assemblage sera de 3 fichiers: another.bundle.js, index.bundle.js, vendors ~ 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
Maintenant, afin de lancer nos applications Web, dans un cas, nous devons connecter les fournisseurs ~ un autre ~ index.bundle.js et index.bundle.js, et dans les seconds fournisseurs ~ un autre ~ index.bundle.js et un autre. bundle.js.
Quel est le problème?
Le problème est le nom des fournisseurs ~ un autre ~ index .bundle.js. Tant que nous avons moins de trois points d'entrée, rien de mal ne se produit. Tout semble logique ici - le bundle contient des modules npm (ils sont également des fournisseurs) et des modules généraux pour index et un autre. Sur chacune des pages, nous incluons 2 fichiers et n'avons aucun problème.
Cependant, si nous avons trois points d'entrée ou plus, il peut y avoir beaucoup plus de nouveaux bundles (ce sont des morceaux) et nous ne connaissons plus leur nombre ou leurs noms. Tout devient encore plus amusant si nous extrayons également des fichiers CSS dans des fichiers séparés. Et c'est ça le problème.
Comment résoudre ce problème?
Après avoir terminé le pack Web, nous n'avons plus de fichiers contenant des informations sur les bundles sur telle ou telle page à connecter. Et dans quel ordre.
Cependant, dans la sortie, nous pouvons trouver ces lignes:
Entrypoint index = vendors~another~index.bundle.js index.bundle.js Entrypoint another = vendors~another~index.bundle.js another.bundle.js
En fait, c'est presque ce dont nous avons besoin. C'est-à-dire webpack sait très bien quels bundles sont nécessaires pour chaque point d'entrée, mais pour une raison quelconque, il ne veut pas partager ces informations avec nous.
Le manifeste ne nous aide pas ici. Oui, nous savons qu'il existe un tel bundle (vendeurs ~ un autre ~ index.bundle.js). Nous savons où il se trouve. Mais nous ne savons pas qui en a besoin. C'est-à-dire le manifeste est inutile.
Ensuite, j'ai décidé que, puisque webpack connaît les informations nécessaires, elles peuvent éventuellement être obtenues à l'aide de plugins. Je n'en ai trouvé aucun prêt à l'emploi et j'ai décidé d'écrire le mien. Et, juste pour faire la démonstration de ce plugin, j'écris cet article.
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 }; }); } }
Dans le fichier webpack.config. (Ts | js), ajoutez un nouveau plugin:
plugins: [ new EntrypointsPlugin({ filename: "entrypoints.json", space: 2 }) ]
et attendez le résultat. Le résultat sera un fichier entrypoints.json avec ce contenu:
{ "index": { "js": ["vendors~another~index.bundle.js", "index.bundle.js"] }, "another": { "js": ["vendors~another~index.bundle.js", "another.bundle.js"] } }
Si extract-css est utilisé, en plus de la section js, il y aura également css.
La dernière chose qui nous reste, lors de la formation de la page HTML, est de lire le fichier entrypoints.json, de trouver le point d'entrée souhaité, de connecter les fichiers js et css à partir des listes correspondantes.
Problème résolu
Quelque chose comme ça.