什么是Tree Shaking

  • Tree Shaking 是一个术语,在计算机中表示消除死代码(dead_code);
  • 最早的想法起源于LISP,用于消除未调用的代码(纯函数无副作用,可以放心的消除,这也是为什么要求我们在进行函数式编程时,尽量使用纯函数的原因之一);
  • 后来Tree Shaking也被应用于其他的语言,比如JavaScript、Dart;

JavaScript的Tree Shaking:

  • 对JavaScript进行Tree Shaking是源自打包工具 rollup(后面我们也会讲的构建工具);
  • 这是因为Tree Shaking依赖于ES Module的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系);
  • webpack2正式内置支持了ES2015模块,和检测未使用模块的能力;
  • 在webpack4正式扩展了这个能力,并且通过 package.json sideEffects 属性作为标记,告知webpack在编译时,哪里文件可以安全的删除掉;
  • webpack5中,也提供了对部分CommonJS的tree shaking的支持;

    https://github.com/webpack/changelog-v5

webpack中如何实现Tree Shaking(JS)

事实上,webpack实现Tree Shaking采用了两种不同的方案:

  • usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化;
  • sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用。

1.usedExports

1)将mode设置为development模式:为了可以看到 usedExports带来的效果,我们需要设置为 development 模式。

因为在 production 模式下,webpack默认的一些优化会带来很大的影响。

2)设置usedExports为true和false对比打包后的代码:
在usedExports设置为true时,会有一段注释:unused harmony export mul;这段注释的意义是什么呢?告知Terser在优化时,可以删除掉这段代码。
这个时候,我们j将minimize设置true:

  • usedExports设置为false时,mul函数没有被移除掉;
  • usedExports设置为 true 时,mul函数有被移除掉;

所以,usedExports实现Tree Shaking是结合Terser来完成的。

  1. optimization: {
  2. usedExports: true, // production自动设置为true
  3. minimize: true,
  4. minimizer: {
  5. ...
  6. }
  7. }

2.sideEffects

sideEffects用于告知webpack compiler哪些模块是有副作用的:

  • 副作用的意思是这里面的代码有执行一些特殊的任务,不能仅仅通过export来判断这段代码的意义;
  • 副作用的问题,在讲React的纯函数时是有讲过的;

package.json中设置 sideEffects 的值:

  • 如果我们将sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports;
  • 如果有一些我们希望保留,可以设置为数组

比如我们有一个format.js、style.css文件:

  • 该文件在导入时没有使用任何的变量来接受;
  • 那么打包后的文件,不会保留format.js、style.css相关的任何代码;
  • 可以在处理css的loader加入sideEffects: true

image.png

3.总结

所以,如何在项目中对JavaScript的代码进行TreeShaking呢(生成环境)?

  • 在optimization中配置usedExports为true,来帮助Terser进行优化;
  • 在package.json中配置sideEffects,直接对模块进行优化;

几个注意点

我们学到为了利用 tree shaking 的优势, 你必须…

  • 使用 ES2015 模块语法(即 import 和 export)
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
  • 在项目的 package.json 文件中,添加 “sideEffects“ 属性。
  • 使用 mode 为 “production” 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

CSS实现Tree Shaking

上面我们学习的都是关于JavaScript的Tree Shaking,那么CSS是否也可以进行Tree Shaking操作呢?

  • CSS的Tree Shaking需要借助于一些其他的插件;
  • 在早期的时候,我们会使用PurifyCss插件来完成CSS的tree shaking,但是目前该库已经不再维护了(最新更新也是在4年前了);
  • 目前我们可以使用另外一个库来完成CSS的Tree Shaking:PurgeCSS,也是一个帮助我们删除未使用的CSS的工具;

安装PurgeCss的webpack插件:
npm install purgecss-webpack-plugin -D

配置PurgeCss

配置这个插件(生产环境):

  • paths:表示要检测哪些目录下的内容需要被分析,这里我们可以使用glob(webpack默认安装了);
  • 默认情况下,Purgecss会将我们的html、body标签的样式移除掉,如果我们希望保留,可以添加一个safelist的属性;
    1. new PurgeCssPlugin({
    2. paths: glob.sync(`${resolveApp("./src")}/**/*`, {nodir: true}),
    3. safelist: function() {
    4. return {
    5. standard: ["body", "html"]
    6. }
    7. }
    8. })
    purgecss也可以对less文件进行处理(所以它是对打包后的css进行tree shaking操作)。