一、解决的问题
Tree-shaking 去除无用代码,从而减小文件体积。
二、什么是Tree Shaking
Tree-shaking (摇树) 是一个术语,通常指通过打包工具"摇"我们的代码,将未引用代码 (Dead Code) "摇" 掉。在 Webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝,虽然依赖了某些模块,但其实只使用其中的某些方法,通过 Tree Shaking ,将没有使用的方法摇掉,这样来达到删除无用代码的目的。
三、原理
程序会从入口文件出发 ,扫描所有的模块依赖,以及模块的子依赖 ,然后将它们链接 起来形成 一个 "抽象语法树 " (AST)。随后,运行所有代码 ,查看哪些代码是用到过的 ,做好标记 。最后,再将 "抽象语法树"中没有用到的代码"摇落" 。经历这样一个过程后,就去除了没有用到的代码。
前提是模块必须采用 ES6 Module 语法,因为 Tree Shaking 依赖 ES6 的静态语法:import 和 export。不同于 ES6 Module,CommonJS 支持动态加载模块 ,在加载前是无法确定模块是否有被调用 ,所以并不支持 Tree Shaking 。如果项目中使用了 babel 的话, @babel/preset-env
默认将模块转换成 CommonJs 语法,因此需要设置 module:false
。
CommonJS 与 ES6 Module 模块的依赖的区别在于,CommonJS 是动态的 ,ES6 Module 是静态的。
CommonJS 导入时,require
的路径参数是支持表达式的,路径在代码执行时是可以动态改变的,所以如果在代码编译阶段就建立各个模块的依赖关系,那么一定是不准确的,只有在代码运行了以后,才可以真正确认模块的依赖关系,因此说CommonJS 是动态的。
ES6 模块不是对象,它的对外接口只是一种静态定义,在代码编译,静态解析阶段就会生成,这样我们就可以使用各种工具对JS模块进行依赖分析,优化代码。
四、sideEffects
webpack v4 开始新增了一个 sideEffects
特性,通过给 package.json
加入 sideEffects: false
声明该包模块是否包含副作用,从而可以为 Tree Shaking 提供更大的优化空间。 举例说明
arduino
js
复制代码
// a.js
// 无副作用,仅仅是单纯的 export
function a () {
console.log('a')
}
export default {
a
}
javascript
js
复制代码
// b.js
function b () {
console.log('b')
}
// 执行了特殊行为
Array.prototype.fun = () => {}
export default {
b
}
如果 a 在 import 后未使用,Tree Shaking 完全可以将其优化掉;但是 b 在 import 后未使用,但因为存在他还执行了为数组原型添加了方法,副作用还是会被保留下来 。这时就需要使用 sideEffects: false
,可以强制标识该包模块不存在副作用,那么不管它是否真的有副作用,只要它没有被引用到,整个 模块/包 都会被完整的移除。
如果你的项目中存在一些副作用代码 b 需要被保留下来,比如 polyfill、css、scss、less 等,可以按下面方法一样配置;保证必要的代码不被 Tree Shaking
json
json
复制代码
// package.json
{
"name": "your-project",
"sideEffects": ["./src/b.js", "*.css"]
}
四、配置条件
- 使用 ES6 Module 语法(即
import
和export
)。 - 确保没有
@babel/preset-env
等工具将 ES6Module 语法转换为 CommonJS 模块。 optimization: { minimize: true, usedExports: true }
。- 使用支持 Tree Shaking 的包。