Tree Shaking概念和原理以及es6 module和commonjs的区别

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博客

 参考文档:

Webpack 原理系列九:Tree-Shaking 实现原理 - 知乎

Tree Shaking原理请参考百度外卖团队的文章

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值