tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
它依赖于 ES2015 模块系统中的静态结构特性,例如 import
和 export
。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。
新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json
的 "sideEffects"
属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯的 ES2015 模块)",由此可以安全地删除文件中未使用的部分
"sideEffects": false:所有文件可以安全的操作,基本不能这么用,因为有CSS
如果你的代码确实有一些副作用,那么可以改为提供一个数组:数组里面是不操作的列表
数组方式支持相关文件的相对路径、绝对路径和 glob 模式
"sideEffects": [ "./src/some-side-effectful-file.js" ]
注意,任何导入的文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader
并导入 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除:
{ "name": "your-project", "sideEffects": [ "./src/some-side-effectful-file.js", "*.css" ] }
压缩输出
通过如上方式,我们已经可以通过 import
和 export
语法,找出那些需要删除的“未使用代码(dead code)”,然而,我们不只是要找出,还需要在 bundle 中删除它们。为此,我们将使用 -p
(production) 这个 webpack 编译标记,来启用 uglifyjs 压缩插件。
注意,--optimize-minimize
标记也会在 webpack 内部调用UglifyJsPlugin
。
从 webpack 4 开始,也可以通过 "mode"
配置选项轻松切换到压缩输出,只需设置为 "production"
。
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') - } + }, + mode: "production" };
实验结果:准备就绪后,然后运行另一个命令 npm run build
,看看输出结果有没有发生改变。
你发现 dist/bundle.js
中的差异了吗?显然,现在整个 bundle 都已经被精简过,但是如果仔细观察,则不会看到 square
函数被引入,但会看到 cube
函数的修改版本(function r(e){return e*e*e}n.a=r
)。
现在,随着 tree shaking 和代码压缩,我们的 bundle 减小几个字节!
虽然,在这个特定示例中,可能看起来没有减少很多,但是,在具有复杂的依赖树的大型应用程序上运行时,tree shaking 或许会对 bundle 产生显著的体积优化。
准备就绪后,然后运行另一个命令 npm run build
,看看输出结果有没有发生改变。
你发现 dist/bundle.js
中的差异了吗?显然,现在整个 bundle 都已经被精简过,但是如果仔细观察,则不会看到 square
函数被引入,但会看到 cube
函数的修改版本(function r(e){return e*e*e}n.a=r
)。现在,随着 tree shaking 和代码压缩,我们的 bundle 减小几个字节!虽然,在这个特定示例中,可能看起来没有减少很多,但是,在具有复杂的依赖树的大型应用程序上运行时,tree shaking 或许会对 bundle 产生显著的体积优化。
结论
为了学会使用 tree shaking,你必须……
- 使用 ES2015 模块语法(即
import
和export
)。 - 在项目
package.json
文件中,添加一个 "sideEffects" 入口。 - 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如
UglifyJSPlugin
)。