使用Webpack 4和代码分离的另一种方法

背景知识


众所周知,随着Webpack 4的发布,代码共享策略已经发生了很大变化。 最好再说一遍,因为 旧的方法刚刚停止工作,而新的方法尚不清楚如何使用。


对于那些仍然不是最新的人,webpack.optimize.CommonsChunkPlugin插件已不存在。 绝对是 相反,建议在配置中写入以下内容


module.exports = { // ... optimization: { splitChunks: { chunks: "all" } } // ... } 

那应该像魔术一样工作。 即 现在,我们不告诉webpack如何处理常见的问题,但他会自己做所有事情,甚至比我们做得更好。


幸福会来。 开个玩笑 不是真的...


基本准备


这是文档中的一个示例:


 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' } } }; 

组装的结果将是3个文件:another.bundle.js,index.bundle.js,供应商〜另一个〜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 

现在,为了启动我们的Web应用程序,在一种情况下,我们必须将供应商连接到另一个〜index.bundle.js和index.bundle.js,在第二个供应商中连接另一个〜index.bundle.js和另一个。 bundle.js。


怎么了


问题是供应商的名称〜另一个〜索引 .bundle.js。 只要我们的入口点少于三个,就不会发生任何不良情况。 这里的一切似乎合乎逻辑-捆绑软件包含npm模块(它们也是供应商)以及用于索引和其他模块的常规模块。 在每个页面上,我们都包含2个文件,没有问题。


但是,如果我们有三个或更多入口点,那么可能会有更多的新捆绑包(它们是大块),并且我们不再知道它们的编号或名称。 如果我们还将css提取到单独的文件中,一切将变得更加有趣。 这就是问题所在。


如何解决这个问题?


完成webpack后,我们没有任何文件包含有关应连接该页面或该页面上的哪个捆绑软件的信息。 并以什么顺序。


但是,在输出中,我们可以找到以下几行:


 Entrypoint index = vendors~another~index.bundle.js index.bundle.js Entrypoint another = vendors~another~index.bundle.js another.bundle.js 

实际上,这几乎是我们所需要的。 即 webpack非常了解每个入口点需要什么捆绑软件,但是由于某种原因,它本身不想与我们共享此信息。


宣言对我们没有帮助。 是的,我们知道有这样一个(供应商〜另一个〜index.bundle.js)包。 我们知道他在哪里。 但是我们不知道谁需要它。 即 清单是没用的。


然后我决定,由于webpack知道必要的信息,因此可以使用插件来获取它。 我没有找到任何现成的,决定写自己的。 而且,仅出于演示此插件的目的,我正在写这篇文章。


 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 }; }); } } 

在webpack.config(Ts | js)文件中,添加一个新插件:


 plugins: [ new EntrypointsPlugin({ filename: "entrypoints.json", space: 2 }) ] 

然后等待结果。 结果将是一个具有以下内容的entrypoints.json文件:


 { "index": { "js": ["vendors~another~index.bundle.js", "index.bundle.js"] }, "another": { "js": ["vendors~another~index.bundle.js", "another.bundle.js"] } } 

如果使用extract-css,则除了js部分外,还将有css。


在形成HTML页面时,剩下的最后一件事情是读取entrypoints.json文件,找到所需的入口点,从相应的列表中连接js和css文件。


问题解决


这样的东西。

Source: https://habr.com/ru/post/zh-CN423693/


All Articles