这是我参与更文挑战的第 2 天,活动详情查看: 更文挑战
Lynne,一个能哭爱笑永远少女心的前端开发工程师。身处互联网浪潮之中,热爱生活与技术。
前言
如果了解过我前面提到过 rollup 系列的这篇文章 - 无用代码去哪了?项目减重之 rollup 的 Tree-shaking,那你一定对 tree-shaking 不陌生了。
原本不支持tree-shaking 的 Webpack 在它的 2.x 也实现了 tree-shaking,好奇心又来了,Webpack 实现 tree-shaking 和 rollup 实现 tree-shaking 的原理是一样的吗?
因为这样的疑问,就有了眼前这篇文章。
Webpack 实现 tree-shaking
快速浏览完官方文档和众多文章后,发现 webpack 实现 tree-shaking 的方式还不止一种!但是,都与 rollup 不同。
rollup 是在编译打包过程中分析程序流,得益于于 ES6 静态模块(exports 和 imports 不能在运行时修改),我们在打包时就可以确定哪些代码时我们需要的。
而 webpack 本身在打包时只能标记未使用的代码而不移除,识别 代码未使用标记 并完成 tree-shaking 的 其实是 UglifyJS、babili、terser 这类压缩代码的工具。简单来说,就是压缩工具读取打包结果,在压缩之前移除未使用的代码。
那么 Webpack 实现 tree-shaking 的过程是怎样的呢?
2.1 初级 tree-shaking
关于最早版本的 Webpack 实现 tree-shaking 可以参考这篇文章 如何在 Webpack 2 中使用 tree-shaking,掘金也有翻译版,当然如果不愿意花时间考古,也可以看下面这一段总结:
- UglifyJS 不支持 ES6 及以上,需要用 Babel 将代码编译为 ES5,然后再用 UglifyJS 来清除无用代码
- 通过 Babel 将代码编译为 ES5,但又要让 ES6 模块不受 Babel 预设(preset)的影响:配置 Babel 预设不转换 module,对应地配置 Webpack 的 plugins 配置
- 为避免副作用,将其标记为 pure(无副作用),以便 UglifyJS 能够处理
{
"presets": [
["env", {
"loose": true, // 宽松模式
"modules": false // 不转换module,保持ES6语法
}]
]
}
module: {
rules: [
{ test: /\.js$/, loader: 'babel-loader' }
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: true
},
output: {
comments: false
},
sourceMap: false
})
]
2.2 tree-shaking 进阶 - Babili
Babili 是基于 Babel 的代码压缩工具,可以识别 ES6+ 的语法,所有 Babel 可以解析的语言特性它都支持。Babili 能将 ES6 代码编译为 ES5,移除未使用的类和函数,就像 UglifyJS 已经支持 ES6 一样。
2.3 tree-shsking 终极 - Terser
从 Webpack4.x 开始采用 terser 替代 UglifyJS 压缩代码,不仅解决了 UglifyJS 不支持 ES6 语法的问题,更是大大提高了速度。到目前最新的 Webpack5.x 版本,terser 已经内置于 Webpack 中,不再需要额外的配置。
webpack 4 没有分析模块的导出和引用之间的依赖关系。webpack 5 有一个新的选项 optimization.innerGraph,在生产模式下是默认启用的,它可以对模块中的标志进行分析,找出导出和引用之间的依赖关系。
同时,可以通过 package.json 有一个特殊的属性 sideEffects 告诉 Webpack 你的代码无副作用。
2.4 小结
但本质上 Webpack 的 tree-shaking 机制并不同于 Rollup。它会包含所有的代码,标记未使用的函数和函数,以便压缩工具能够移除。这就是对所有代码都进行 tree-shake 的困难之处。使用默认的压缩工具 UglifyJS / terser,它仅移除未使用的函数和变量;Babili 支持 ES6,可以用它来移除类,但必须特别注意第三方模块发布的方式是否支持 tree-shaking。