tree shaking的原理都说是通过ES6 Module来进行静态分析,可是具体是如何静态分析的?
2 个回答
前言
什么是Tree Shaking
Tree-Shaking 是一种基于ES Module规范的 Dead Code Elimination(死代码优化)技术,它会在运行过程中静态分析模块之间的导入导出,确定 ESM 模块中哪些导出值未曾其它模块使用,并将其删除,以此实现打包产物的优化(减少打包体积)。
什么是ES Module
JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
简单比较如下:
- ES Module 输出的是值的引用,而 CommonJS 输出的是值的拷贝;
- ES Module 是编译时执行,而 CommonJS 模块是在运行时加载;
- ES6 Module可以导出多个值,而CommonJs 是单个值导出;
- ES6 Module 静态语法只能写在顶层,而CommonJs 是动态语法可以写在判断里;
- ES6 Module的 this 是 undefined,而CommonJs 的 this 是当前模块;
静态化的意义:模块的依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,Tree-Shaking 正是基于这个前提才有实现的可能。
Tree Shaking实现原理
以Webpack中Tree-Shaking处理为例,其实现分为如下阶段,不同阶段执行不同流程:
一、收集模块导出
收集各个模块导出变量并记录到模块依赖关系图变量ModuleGraph中;
二、标记模块导出
模块导出信息收集完毕后,根据entry,Webpack 标记出各个模块的导出列表中,哪些导出值有被其它模块用到,哪些没有用到,
三、生成代码
经过前面的收集与标记步骤后,Webpack 已经在 ModuleGraph 体系中清楚地记录了每个模块都导出了哪些值,每个导出值有没有被引入使用。接下来,Webpack 会根据导出值的使用情况生成不同的代码:
模块导出列表中被使用的值都会定义在 __webpack_exports__
对象中;
四、 删除 Dead Code
经过前面几步操作之后,模块导出列表中未被使用的值都不会定义在__webpack_exports__
对象中,标记形成一段不可能被执行的 Dead Code 效果。
在此之后,将由 Terser、UglifyJS 等 DCE 工具“摇”掉这部分无效代码,构成完整的 Tree Shaking 操作;
其中UglifyJS 等 DCE 工具“摇”掉这部分无效代码包括:
- 标记的没有使用的函数;
- 没有使用的变量;
总结
现在所提到的Tree Shaking都是在ES Module标准只上的:静态编译,确定输入输出值;
Tree Shaking主要流程可以简化为:搜集依赖—>标记引用—>清除DeadCode。