SyMind / learning

路漫漫其修远兮,吾将上下而求索。
9 stars 1 forks source link

Parcel 的 Tree shaking 实现 #29

Open SyMind opened 2 years ago

SyMind commented 2 years ago

原文:https://medium.com/@devongovett/parcel-v1-9-0-tree-shaking-2x-faster-watcher-and-more-87f2e1a70f79#4ed3

Parcel 编译后完全移除掉模块系统,提升所有模块到同一顶级作用域中,清除掉生产包中的无用代码。Parcel 同时支持 ES6 和 CommonJS 模块的 Tree shaking。

Parcel Tree shaking 的实现包含以下四个阶段:

  1. 提升阶段 —— 独立地发生在每个文件中。在这个阶段中,Parcel 对所有顶级变量进行唯一重命名,从而避免与其它模块中的变量产生冲突,对模块中的导入导出标识生成元数据。
  2. 组合阶段 —— 当所有的文件被处理完毕后,Parcel 以正确的顺序将所有模块组合在一起。在这里需要考虑许多边界问题,尤其是对于 CommonJS 模块,例如循环依赖、副作用、内嵌的 require。最终生成一个包含所有代码的单一文件。
  3. 链接阶段 —— 当所有文件通过正确的顺序被组合完毕后,Parcel 使用在提升阶段中产生的元数据,将各模块的导入和导出链接起来。它会处理 CommonJS 和 ES6 模块之间的互操作性,因此您可以 import 一个 CommonJS 文件,或者 require 一个 ES6 模块。
  4. 清理阶段 —— 在这个阶段中,将删除无用的声明和顶级变量。大多数的压缩工作在文件层面,发生在提升阶段后。剩下的唯一工作就是清理掉顶级作用域中没有在模块之间使用的声明,并压缩剩下的顶级变量名。

下面示例展示了 Parcel 的 Tree shaking 如何移除未使用的导出,并重命名变量,以便它们不与其他模块冲突。输出包中没有模块包装函数,只有一个 IIFE 包装了整个包,模块的成本为零。

// index.js
import {add} from './math';
console.log(add(2, 3));

// math.js
export function add(a, b) {
    return a + b;
}
export function sub(a, b) {
    return a - b;
}

// 打包的输出
(function () {
// ASSET: math.js
function $3$export$add(a, b) {
    return a + b;
}

// ASSET: index.js
console.log($3$export$add(2, 3));
})();

// 压缩后
(function () {function a($, r) {return $+r}console.log(a(2,3));})();

Parcel 也支持 package.json 中的 sideEffects: false 标记,这表明库作者告知 Parcel 和其它打包工具,该库在初始化阶段中不包含任何副作用,允许打包工具更加有效地判断需要包含哪些代码。