几个月前,我的任务是将我们组的 Vue.js 项目构建配置升级到 Webpack 4。我们的主要目标之一是利用 tree-shaking 的优势,即 Webpack 去掉了实际上并没有使用的代码来减少包的大小。现在,tree-shaking 的好处将根据你的代码库而有所不同。由于我们的几个架构决策,我们从公司内部的其他库中提取了大量代码,而我们只使用了其中的一小部分。
我写这篇文章是因为恰当地优化 Webpack 并不简单。一开始我以为这是一种简单的魔法,但后来我花了一个月的时间在网上搜索我遇到的一系列问题的答案。我希望通过这篇文章,其他人会更容易地处理类似问题。
先说好处
在讨论技术细节之前,让我先总结一下好处。不同的应用程序将看到不同程度的好处。主要的决定因素是应用程序中死代码的数量。如果你没有多少死代码,那么你就看不到 tree-shaking 的多少好处。我们项目里有很多死代码。
在我们部门,最大的问题是共享库的数量。从简单的自定义组件库,到企业标准组件库,再到莫名其妙地塞到一个库中的大量代码。很多都是技术债务,但一个大问题是我们所有的应用程序都在导入所有这些库,而实际上每个应用程序都只需要其中的一小部分
总的来说,一旦实现了 tree-shaking,我们的应用程序就会根据应用程序的不同,缩减率从25%到75%。平均缩减率为52%,主要是由这些庞大的共享库驱动的,它们是小型应用程序中的主要代码。
同样,具体情况会有所不同,但是如果你觉得你打的包中可能有很多不需要的代码,这就是如何消除它们的方法。
没有示例代码仓库
对不住了各位老铁,我做的项目是公司的财产,所以我不能分享代码到 GitHub 仓库了。但是,我将在本文中提供简化的代码示例来说明我的观点。
因此,废话少说,让我们来看看如何编写可实现 tree-shaking 的最佳 webpack 4 配置。
什么是死代码
很简单:就是 Webpack 没看到你使用的代码。Webpack 跟踪整个应用程序的 import/export 语句,因此,如果它看到导入的东西最终没有被使用,它会认为那是“死代码”,并会对其进行 tree-shaking 。
死代码并不总是那么明确的。下面是一些死代码和“活”代码的例子,希望能让你更明白。请记住,在某些情况下,Webpack 会将某些东西视为死代码,尽管它实际上并不是。请参阅《副作用》一节,了解如何处理。
// 导入并赋值给 JavaScript 对象,然后在下面的代码中被用到 // 这会被看作“活”代码,不会做 tree-shaking import Stuff from './stuff'; doSomething(Stuff); // 导入并赋值给 JavaScript 对象,但在接下来的代码里没有用到 // 这就会被当做“死”代码,会被 tree-shaking import Stuff from './stuff'; doSomething(); // 导入但没有赋值给 JavaScript 对象,也没有在代码里用到 // 这会被当做“死”代码,会被 tree-shaking import './stuff'; doSomething(); // 导入整个库,但是没有赋值给 JavaScript 对象,也没有在代码里用到 // 非常奇怪,这竟然被当做“活”代码,因为 Webpack 对库的导入和本地代码导入的处理方式不同。 import 'my-lib'; doSomething();
用支持 tree-shaking 的方式写 import
在编写支持 tree-shaking 的代码时,导入方式非常重要。你应该避免将整个库导入到单个 JavaScript 对象中。当你这样做时,你是在告诉 Webpack 你需要整个库, Webpack 就不会摇它。
以流行的库 Lodash 为例。一次导入整个库是一个很大的错误,但是导入单个的模块要好得多。当然,Lodash 还需要其他的步骤来做 tree-shaking,但这是个很好的起点。
// 全部导入 (不支持 tree-shaking) import _ from 'lodash'; // 具名导入(支持 tree-shaking) import { debounce } from 'lodash'; // 直接导入具体的模块 (支持 tree-shaking) import debounce from 'lodash/lib/debounce';
基本的 Webpack 配置
使用 Webpack 进行 tree-shaking 的第一步是编写 Webpack 配置文件。你可以对你的 webpack 做很多自定义配置,但是如果你想要对代码进行 tree-shaking,就需要以下几项。
首先,你必须处于生产模式。Webpack 只有在压缩代码的时候会 tree-shaking,而这只会发生在生产模式中。
其次,必须将优化选项 “usedExports” 设置为true。这意味着 Webpack 将识别出它认为没有被使用的代码,并在最初的打包步骤中给它做标记。