webpack 拾翠:充分利用 CommonsChunkPlugin()

webpack 核心团队隔三差五地就会在 Twitter 上做一些寓教于乐的技术分享javascript

Markdown

此次的“游戏规则”很简单:安装 webpack-bundle-analyzer,生成一张包含全部 bundles 信息的酷炫图片分享给我,而后 webpack 团队会帮忙指出任何潜在的问题。前端

咱们发现了什么?

最多见的问题是代码重复:库、组件、代码在多个(同步的、异步的)bundles 中重复出现。java

案例一:不少重复代码的 vendor bundles

Markdown

Swizec Teller 分享了一个构建图(其实是对 8-9 个独立单页应用的构建)。在众多例子中我决定选择这一个,由于咱们能够从中学到不少技术,下面让咱们来仔细分析一下:node

距离 “FoamTree” 图标最近的是应用自己的代码,而其余全部 node_modules 的代码则是左边那些以 "_vendor.js" 结尾的。react

单从这幅图(不须要看实际配置文件)中咱们就能推断出不少事情。jquery

每一个单页应用都运用了一个 new CommonsChunkPlugin ,并以其 entry 和 vendor 代码为目标。这会生成两个 bundles,一个只包含 node_modules 里面的代码,另外一个则只包含应用自己的代码。(Swizec Teller)甚至还提供了部分配置信息:android

Markdown

Object.keys(activeApps)
  .map(app => new webpack.optimize.CommonsChunkPlugin({
    name: `${app}_vendor`,
    chunks: [app],
    minChunks: isVendor
  }))复制代码

其中 activeApps 变量极可能是用来表示独立入口点的。webpack

能够优化的地方

下面几个画圈的是能够优化的地方。ios

“Meta” 缓存

从上图能够看出,许多大型代码库(例如 momentjs、lodash、jquery 等)同时被 6 个(甚至更多) bundles 用到了。将全部 vendors 打包到一个独立 bundle 中的策略是很好的,但其实对全部 vendor bundles 也应该采起一样的策略。git

我建议 Swizec 将以下插件添加到插件数组的末尾

new webpack.optimize.CommonsChunkPlugin({
  children: true, 
  minChunks: 6
})复制代码

这是在告诉 webpack:

嘿 webpack,请检查全部的 chunks(包括那些由 webpack 生成的 vendor chunks),找出那些在 6个及6个以上 chunks 中都出现过的模块,并将其移到一个独立的文件中。

Markdown

Markdown

如你所见,如今全部符合要求的模块都被抽离到一个独立的文件中,Swizec 指出这个应用程序大小下降了 17%。

案例二:异步 chunks 中的重复 vendors

Markdown

就总体代码体积来讲,这种数量的重复并不严重;可是,若是你看到下面这张完整大图,你就会发现每个异步 chunk 中都有 3 个如出一辙的模块。

异步 chunks 是指那些文件名中包含 "[number].[number].js" 的 chunk。

如上图所示,四五十个异步 bundles 都用到了两三个一样的组件,咱们该如何利用 CommonsChunkPlugin 来解决此问题呢?

建立一个异步 Commons Chunk

解决方法和第一个案例中的相似,可是须要将配置选项中的 async 属性设为 true,代码以下:

new webpack.optimize.CommonsChunkPlugin({
  async: true, 
  children: true, 
  filename: "commonlazy.js"
});复制代码

相似地 —— webpack 会扫描全部 chunks 并检查公共模块。因为设置了 async: true,只有代码拆分的 bundles 会被扫描。由于咱们并无指明 minChunks 的值,因此 webpack 会取其默认值 3。综上,上述代码的含义是:

嘿 webpack,请检查全部的普通(即懒加载的)chunks,若是某个模块出如今了 3 个或 3 个以上的 chunks 中,就将其分离到一个独立的异步公共 chunk 中去。

效果以下图所示:

Markdown

如今异步 chunks 都很是的小,而且全部代码都被聚合到 commonlazy.js 文件中去了。由于这些 bundles 原本就很小了, 首次访问可能都察觉不到代码体积的变化。如今,每个代码拆分的 bundle 所需携带的数据更少了;并且,经过将这些公共模块放到一个独立可缓存的 chunk 中,咱们节省了用户加载时间,减小了须要传输的数据量(data consumption)。

更多控制:minChunks 函数

Markdown

那若是你想要跟多的控制权呢?某些状况下你可能并不想要一个单独的共享 bundle,由于并非每个懒加载/入口 chunk 都要用到它。minChunks 属性的取值也能够是一个函数!该函数能够用做“过滤器”,决定将哪些模块加到新建立的 bundle 中去。示例以下:

new webpack.optimize.CommonsChunkPlugin({
  filename: "lodash-moment-shared-bundle.js", 
  minChunks: function(module, count) { 
    return module.resource && /lodash|moment/.test(module.resource) && count >= 3
  }
})复制代码

上例含义是:

呦 webpack,若是你发现某个模块的绝对路径和 lodash 或 momentjs 相匹配而且出如今了 3 个(或 3 个以上)独立的 entries/chunks 中,请将其抽取到一个独立的 bundle 中去。

经过设置 async: true,你也能够将此方法应用到异步 bundles 中。

更多更多控制

Markdown

有了这种 minChunks,你就能够为特定的 entries 和 bundles 生成更小的可缓存 vendors 的子集。最终,你的代码看起来大概就像这样:

function lodashMomentModuleFilter(module, count) {
  return module.resource && /lodash|moment/.test(module.resource) && count >= 2;
}

function immutableReactModuleFilter(module, count) {
  return module.resource && /immutable|react/.test(module.resource) && count >=4
}

new webpack.optimize.CommonsChunkPlugin({
  filename: "lodash-moment-shared-bundle.js", 
  minChunks: lodashMomentModuleFilter
})

new webpack.optimize.CommonsChunkPlugin({
  filename: "immutable-react-shared-bundle.js", 
  minChunks: immutableReactModuleFilter
})复制代码

没有银弹!

CommonsChunkPlugin() 当然很强大,但要记住本文中的例子都是针对特定应用的。所以,在复制-粘贴这些代码片断以前,请先听听 Sam SacconePaul IrishMPDIA 的建议,避免用错了方法。

在应用解决方法以前,必定要理解方法背后的思路!

哪里还有更多例子?

上述只是 CommonsChunkPlugin() 的部分用例,更多资源请参考咱们 webpack/webpack core GitHub 仓库中的 [/examples](https://github.com/webpack/webpack/tree/master/examples) 目录。若是你还有其余好想法,欢迎 Pull Request

没时间贡献代码?但愿以其余方式作贡献?向咱们的 open collective 捐款,即刻成为赞助商。Open Collective 不只为核心团队提供支持,同时也帮助那些为提高咱们社区质量而花费了大量宝贵的空闲时间的贡献者们!❤


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索