Antecedentes
Não é segredo que, com o lançamento do Webpack 4, a estratégia de compartilhamento de código mudou muito. É ainda melhor dizer que foi inventado novamente, porque a abordagem antiga parou de funcionar e a nova não está clara como usá-la.
Para aqueles que ainda não estão atualizados, o plug-in webpack.optimize.CommonsChunkPlugin desapareceu. Absolutamente. Em vez disso, é recomendável escrever o seguinte na configuração:
module.exports = {
Isso deve funcionar como mágica. I.e. Agora, não dizemos ao webpack o que fazer como um pedaço comum, mas ele fará tudo sozinho e ainda melhor que nós.
E a felicidade virá. Uma piada. Na verdade não ...
Preparações básicas
Aqui está um exemplo da documentação:
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' } } };
O resultado da montagem será de 3 arquivos: another.bundle.js, index.bundle.js, fornecedores ~ outro ~ 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
Agora, para iniciar nossos aplicativos da Web, em um caso, devemos conectar fornecedores ~ outro ~ index.bundle.js e index.bundle.js, e nos segundos fornecedores ~ outro ~ index.bundle.js e outro. bundle.js.
Qual é o problema?
O problema é o nome de fornecedores ~ outro ~ índice .bundle.js. Enquanto tivermos menos de três pontos de entrada, nada de ruim acontece. Tudo parece lógico aqui - o pacote contém módulos npm (eles também são fornecedores) e módulos gerais para índice e outro. Em cada uma das páginas, incluímos 2 arquivos e não temos problemas.
No entanto, se tivermos três ou mais pontos de entrada, poderá haver muito mais novos pacotes configuráveis (eles são pedaços) e não saberemos mais o número ou os nomes deles. Tudo se torna ainda mais divertido se também extrairmos o CSS em arquivos separados. E esse é o problema.
Como resolver este problema?
Depois de concluir o webpack, não temos mais arquivos que contenham informações sobre quais pacotes nesta ou naquela página devem ser conectados. E em que ordem.
No entanto, na saída, podemos encontrar estas linhas:
Entrypoint index = vendors~another~index.bundle.js index.bundle.js Entrypoint another = vendors~another~index.bundle.js another.bundle.js
De fato, isso é quase o que precisamos. I.e. O webpack sabe muito bem quais pacotes são necessários para cada ponto de entrada, mas, por algum motivo, ele próprio não deseja compartilhar essas informações conosco.
O manifesto não nos ajuda aqui. Sim, sabemos que existe um pacote (fornecedores ~ outro ~ index.bundle.js). Nós sabemos onde ele está. Mas não sabemos quem precisa. I.e. o manifesto é inútil.
Então decidi que, como o webpack conhece as informações necessárias, elas podem ser obtidas usando plugins. Não encontrei nenhum já pronto e resolvi escrever o meu. E, apenas para demonstrar este plugin, estou escrevendo este artigo.
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 }; }); } }
No arquivo webpack.config. (Ts | js), adicione um novo plug-in:
plugins: [ new EntrypointsPlugin({ filename: "entrypoints.json", space: 2 }) ]
e aguarde o resultado. O resultado será um arquivo entrypoints.json com este conteúdo:
{ "index": { "js": ["vendors~another~index.bundle.js", "index.bundle.js"] }, "another": { "js": ["vendors~another~index.bundle.js", "another.bundle.js"] } }
Se extract-css for usado, além da seção js, também haverá css.
A última coisa que resta para nós, ao formar a página HTML, é ler o arquivo entrypoints.json, encontrar o ponto de entrada desejado, conectar arquivos js e css nas listas correspondentes.
Problema resolvido
Algo assim.