Webpack之treeShaking分析之道

webpack原理机制系列

Posted by Cycle263 on March 21, 2019

treeshaking

tree shaking 字面可以理解为摇树,所做的优化就是,过滤不必要和未用到的打包。

  • 背景

Tree shaking一直是一个美丽而遥不可及的话题,它是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。可以简单地理解为摇树,抖落掉枯萎无用的树叶。

  • side effects

影响tree shaking的根本原因在于side effects(副作用),其中最广为人知的一条side effect就是动态引入依赖的问题。ES6其实也提供import()方法支持动态引入依赖,所以以下写法其实也是完全可行的。

Webpack 4官方提供了sideEffects属性,通过将其设置为false,可以主动标识该类库中的文件只执行简单输出,并没有执行其他操作,可以放心shaking。除了可以减小bundle文件的体积,同时也能够提升打包速度。为了检查side effects,Webpack需要在打包的时候将所有的文件执行一遍。而在设置sideEffects之后,则可以跳过执行那些未被引用的文件。

webpack 4之前,可以考虑在babel中开启loose模式,或者去掉babel-loader,也就是说不需要 babel 把 ES Module 转换成曾经的 commonjs 模块了,要使用 Tree Shaking,关闭 babel 默认的模块转义,避免babel转化产生新的副作用,导致shaking not working。然后webpack打包结束后,再执行babel编译文件。

  • 依赖包shaking

目前大量的第三方库,都采用了同时提供两份代码,一份commonjs模块化代码,一份ES6模块化代码,并在package.json里指出两个入口。jsnext:main作为ES6模块化代码的入口已经成为社区的一个默认约定。

// redux为例,有两份代码
node_modules/redux/es/index.js
node_modules/redux/lib/index.js

// package.json
{
  "main": "lib/index.js",
  "jsnext:main": "es/index.js", 
}

// webpack.config.js
{
  resolve: {
    "mainFields": ["main", "jsnext:main"]   // 配置顺序决定优先用哪份代码
  }
}
  • 失效情况

必须采用ES6模块化语法的代码,因为ES6模块化的代码是静态的,webpack才能分析出需要剔除哪些代码。