1、原理

tree shaking是基于es module的静态分析,在编译期间就确定了模块(module)使用情况,并且配合解构赋值能够确定module中哪些方法用到了,哪些没有用到。然后对用到的和没用到的进行标记,在压缩阶段删除没有用到的部分。

2、作用

删除项目中没有用到的代码,有针对性的优化代码来达到最小的bundle size。

3、基本使用

开发环境下

webpack.config.js设置mode、optimization.usedExports、devtool属性。

module.exports = {
  mode: 'development',
  optimization: {
    usedExports: true
  },
  devtool: 'inline-source-map',
  entry: {
    app: "./src/index.js",
  },
  ...
}

这里设置devtoolinline-source-map主要是为了以源代码的方式查看export使用情况。对于未使用的export,optimization.usedExports会添加unused harmony export square的注释

正式环境

webpack.config.js设置mode、optimization.usedExports、devtool属性。

module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true
  },
  devtool: '(none)',
}

mode设置为production后会开启压缩模式,查找压缩后代码中也只是引入了使用的代码。

4、使用tree shaking的注意事项

  • 使用es module的模块规范,使用解构赋值。

    tree shaking是机遇es module的静态分析,所以代码必须使用esm规范。我们在业务代码中一般都会基于esm规范,但是对于第三方的插件不一定,部分第三方插件基于commonJS规范。

  • 开启optimization.usedExports属性。

    optimization: {
        usedExports: true
      },
    

    该属性主要是用于产生标记,标记哪些export使用到了,哪些export没有被使用到。

  • 使用压缩插件。

    webpack4+ 直接将mode设置为production即可开启压缩。

  • tree shaking只能做到export级别。

    如果模块导出一个对象,我们在使用时可能只使用到了对象中的几个属性或者方法,这时候是不能被tree shaking作用的。

    为了更好地配合tree shaking,我们在编码过程中尽量不要export出对象使用,这样能够保证tree shaking打包成最小的bundle。

  • 不需要tree shaking的模块,可以在package.json中添加sideEffects进行配置。

    如果所有的模块都不包含副作用,可以直接将该属性设置为false,来告诉webpack可以安全删除未用到的export。

    如果有些模块有副作用,我们不想让webpack去删除这些export,可以在sideEffects中提供一个数组

    {
      "name": "your-project",
      "sideEffects": [
        "./src/some-side-effectful-file.js",
        "*.css"
      ]
    }
    

    注意,所有导入文件都会受到 tree shaking 的影响。这意味着,如果在项目中使用类似 css-loader *并 import 一个 CSS 文件,则需要将其添加到 side effect 列表中,以免在生产模式中无意中将它删除。

5、使用lodash-es 代替 lodash

我们在项目中可能会使用到lodash的一些功能,进而引入了整个lodash,而lodash是基于commonJS的第三方包,即使使用解构赋值也不会触发tree shaking,导致打包的bundle体积会大很多。

这里推荐使用lodash-es,lodash-es所提供的api与lodash相同,是基于ESM规范的。这里使用解构赋值的方式仅引入使用到的接口,很大程度上减小构建的bundle体积。