Tree Shaking只支持ES模块的使用,不支持require这种动态引入模块的方式。
es的import引入是静态引入,commonjs的require引入是动态引入。
前端中的tree-shaking可以理解为通过工具"摇"我们的JS文件,将其中用不到的代码"摇"掉,是一个性能优化的范畴。具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。
开发环境:webpack.config.js文件中添加optimization选项。生产模式:不用配置下面的属性(默认)
optimization: {
usedExports: true
}
package.json文件中添加sideEffects选项
"sideEffects": false, //对所有的模块都进行Tree Shaking
如果需要对某个模块不进行Tree Shaking
"sideEffects": ["@babel/poly-fill"], //该模块不进行Tree Shaking
// 所有文件都有副作用,全都不可 tree-shaking
{
"sideEffects": true
}
// 没有文件有副作用,全都可以 tree-shaking
{
"sideEffects": false
}
// 只有这些文件有副作用,所有其他文件都可以 tree-shaking,但会保留这些文件
{
"sideEffects": [
"./src/file1.js",
"./src/file2.js"
]
}
为什么某些引入模块不希望进行Tree Shaking呢?
下面引入的style.css模块,如果也使用tree shaking,由于css文件没有导出任何模块,那么就有可能在打包的时候该引入模块就被摇晃掉了,导致bug。
在package.json中进行配置,即匹配到的任何css文件都不进行Tree Shaking
不使用Tree Shaking打包时,可以看到打包文件中exports provided: add, mins两种方法
按照上述配置后打包后,可以看到exports used: add,虽然导出了两个方法,但是仅仅使用了add方法,在开发环境developmeng下仍就可以看到导出的代码中有没有使用到的minus方法,在生产环境production环境中会进行代码的删减。
Treeshaking原理:
实现原理大致有以下三步
1、收集模块导出的内容
(1)ESM 导出语句会转换为 Dependency 对象,记录到 module
对象的 dependencies
集合。
(2)webpack使用FlagDependencyExportsPlugin
插件从 entry 入口开始,遍历所有 module
对象,将其dependencies集合中的导出值,放入 ModuleGraph 中存储,完成模块导出内容的收集。
2、标记模块导出的内容
模块导出信息收集完毕后,Webpack 需要标记出各个模块哪些导出值有被其它模块用到,哪些没有:
webpack使用在 FlagDependencyUsagePlugin
插件中,逐步遍历 ModuleGraph 存储的所有moudle的导出值,每个导出值会被编译器方法(compilation.getDependencyReferencedExports
方法)所检验,确定其对应是否被其它模块使用,如果被其他模块所使用,将该导出值放入到webpack导出对象中( __webpack_exports__
),未被使用的值都不会定义在 __webpack_exports__
对象中,形成一段不可能被执行的 Dead Code 效果。
3、删除无效代码
由 Terser、UglifyJS 等 DCE 工具“摇”掉这部分无效代码,构成完整的 Tree Shaking 操作。
webpack4和webpack5中tree shaking的区别
webpack4:
1、Tree Shaking只支持ES模块的使用,不支持require这种动态引入模块的方式。
2、只分析浅层的模块导出与引入关系,进行dead-code的去除。
webpack5:
1、Webpack 5 中增加了对一些 CommonJS 风格模块代码的静态分析功功能。
支持 exports.xxx、this.exports.xxx、module.exports.xxx 语法的导出分析。
支持 object.defineProperty(exports, "xxxx", ...) 语法的导出分析。
支持 require('xxxx').xxx 语法的导入分析。
2、支持对嵌套引入模块的依赖分析优化,还增加了分析模块中导出项与导入项的依赖关系的功能。
通过 optimization.innerGraph(生产环境下默认开启)选项,Webpack 5 可以分析特定类型导出项中对导入项的依赖关系,从而找到更多未被使用的导入模块并加以移除
示例:
// a.js
function a () {
console.log('a')
}
function b () {
console.log('b')
}
export default {
a, b
}
// index.js
import a from './a'
// 使用a变量
console.log(a.a())
console.log('hello world');
删除了没有使用到的b函数,正确的保留了a函数。注意webpack4是做不到这一点的,只有webpack5才又这个功能。webpack 4 没有分析模块的导出和引用之间的依赖关系。
其他示例:Webpack 5 中的优化细节_Qianliwind的博客-CSDN博客