Skip to content

webpack 的tree-shaking 对 commonjs 和 es6 module 都生效么,原理是 #71

@ChaoCSSun

Description

@ChaoCSSun

why?

移除javascript 上下文中未使用的代码,依赖于ES2015模块语法的静态结构

WebPack 5 对require(commonjs)的treeshaking 做了支持

原理:
ES6模块的依赖关系是确定的,运行时状态无关 可以进行静态的分析,这是tree-shanking 的原理

以前的webpack4 比较简单,即判定import 的是否在这个模块内出现过

**DCE 作用域模块内(webpack 的DCE 通过 uglifyjs 完成), Tree-shaking 则是打包的时候通过模块直接的信息打包必须的代码

DCE:**

AST 对JS 代码进行语法分析后得出的语法书 Abstract Syntax Tree。 AST 语法书可以把一段JS 代码每一句专户成树中的一个节点,
DCE Dead code 保持运行结果不变的前提下 剔除无用的代码
主要包括:
程序中没有执行的代码,导致dead variable,写入变量之后不再用读取的diam

TreeShaking 是 rollup作者提出的

TreeSaking?
CommonJs 的设计灵活 对静态的分析不友好

ES6 Module 诸多限制:只能分析在顶部 Import export 语法 保证导出的变量 不会有get set奇怪的东西
而ComomJs 的require 语法 可以在文件的任意位置调用

根本的原理是 作用域分析

编译领域很多种高达静态分析,而对js 则没有大的基于数据流的分析优化器,所以只能处理顶层

image

所谓作用域分析,就是可以分析出代码里面变量所属的作用域以及他们之间的引用关系。有了这些信息,就可以推导出导出变量和导入变量之间的引用关系。

在编写支持 tree-shaking 的代码时,导入方式非常重要。你应该避免将整个库导入到单个 JavaScript 对象中。当你这样做时,你是在告诉 Webpack 你需要整个库, Webpack 就不会摇它。

以流行的库 Lodash 为例。一次导入整个库是一个很大的错误,但是导入单个的模块要好得多。当然,Lodash 还需要其他的步骤来做 tree-shaking,但这是个很好的起点。

// 全部导入 (不支持 tree-shaking)
import _ from 'lodash';
// 具名导入(支持 tree-shaking)
import { debounce } from 'lodash';
// 直接导入具体的模块 (支持 tree-shaking)
import debounce from 'lodash/lib/debounce';

webpack tree shaking副作用
pure_funcs
webpack.config.js 增加参数pure_funcs,告诉webpack 那些函数是没有副作用的,你可以放心删除:

plugins: [
new UglifyJSPlugin({
uglifyOptions: {
compress: {
pure_funcs: ['Math.floor']
}
}
})
],
Math.floor这类全局方法不会重命名,才会生效。因此适用性不算太强。

package.json 的 sideEffects
webpack 4 在 package.json 新增了一个配置项叫做sideEffects, 值为false表示整个包都没有副作用;或者是一个数组列出有副作用的模块。详细的例子可以查看 webpack 官方提供的例子。

{
"name": "your-project",
"sideEffects": false}
这种方式是通过 package.json 的 "sideEffects" 属性来实现的。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions