天下武功,唯快不破!最新版的 antd 以及 vue 都对 Tree Shaking 提供了支持。我们内部的组件在支持这部分功能时,也专门梳理了相关的特性。这是四月份写的文章了,长时间不用就会忘,复习一下!
JS 文件绝大多数需要通过网络进行加载,然后执行。DCE(dead code elimination)可以使得加载文件的大小更小,整体执行时间更短。tree shaking 就是通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import
和 export
。
原理
ESM
- import 只能作为模块顶层的语句出现
- import 的模块名只能是字符串常量
- import binding 是 immutable 的
这就是区别于CMJ,ESM 独有的静态分析特性。等等,那什么是静态分析呢,就是不执行代码。CMJ 中的 require,只有执行以后才知道引用的是什么模块。
保证了依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析。静态分析会在绘制依赖图时做DCE,减少打包体积。
ESM 也支持动态引入,类似于下面这种引入方式是不支持Tree Shacking的。
if (false) {
import('./a.js').then(() => {
// ...})
} else {
// ...
}
// antd.js
var emptyObject = {};
if (true) {
Object.freeze(emptyObject);
}
module.exports = emptyObject;
Dead Code
Dead Code 通常是指:
- 代码不会被执行
- 代码执行的结果不会被用到
- 代码只会影响死变量(只写不读)
// 导入并赋值给 JavaScript 对象,但在接下来的代码里没有用到
// 这就会被当做“死”代码,会被 tree-shaking
import Stuff from './stuff';
doSomething();
// 导入但没有赋值给 JavaScript 对象,也没有在代码里用到
// 这会被当做“死”代码,会被 tree-shaking
import './stuff';
doSomething();
// 全部导入 (不支持 tree-shaking)
import _ from 'lodash';
// 具名导入(支持 tree-shaking)
import { debounce } from 'lodash';
// 直接导入具体的模块 (支持 tree-shaking)
import debounce from 'lodash/lib/debounce';
// 导入并赋值给 JavaScript 对象,然后在下面的代码中被用到
// 这会被看作“活”代码,不会做 tree-shaking
import Stuff from './stuff';
doSomething(Stuff);
// 导入整个库,但是没有赋值给 JavaScript 对象,也没有在代码里用到
// 非常奇怪,这竟然被当做“活”代码,因为 Webpack 对库的导入和本地代码导入的处理方式不同。
import 'my-lib';
export { default as Title } from './Title';
export { default as Options } from './Options';
export { default as AddonArea } from './AddonArea';
export { default as Answer } from './AddonArea/Answer';
export { default as Analysis } from './AddonArea/Analysis';
export { default as OriginalText } from './AddonArea/OriginalText';
export { default as Labels } from './AddonArea/Labels';
这样的文件结构是无法进行 tree-shaking 的, 因为没有 import?!
自执行的模块 import
自执行模块我们通常会使用 import 'xxx'
来进行模块引用,而不进行显式的调用。因此模块本身就有副作用。
import 'utils/refresh'
对于这种模块可以这样处理:
- 在 sideEffects 中通过数组声明,使其在 Tree Shaking 的范围之外
- 模块改造,暴露成员支持显式调用
unused harmony export
如果该模块被标识为 unused harmony export,则说明没有外部引用使用到该成员,webpack 认为是可以安全去除的。harmony export
部分被标识为 harmony export 的模块也会被去除。这个是跟 UglifyJS 的机制有关系。没有提供导出成员的模块
// ./src/modules/edu-discount/seckill/index.ts
import * as SeckillTypes from './types';
export { SeckillTypes };
对于只有暴露的成员,但是没有被引用的成员,这种模块会被直接删除。
- [x] exports provided
- [ ] exports used
配置
babel的配置文件
{