摇树 tree-shaking

我们可能会向 bundle 添加代码,而这些代码在应用程序中的任何地方都没有使用。
可以消除这一段死代码,以减少包的大小,并防止不必要地加载更多数据!

在将死代码添加到包之前消除死代码的过程称为摇树

尽管摇树适用于像数学模块这样的简单模块,但在某些情况下摇树可能会很棘手。


概念

摇树旨在从最终的 JavaScript 包中删除永远不会使用的代码。
如果做得好,它可以减少 JavaScript 包的大小并降低下载、解析和(在某些情况下)执行时间。
对于大多数使用模块捆绑器(如 webpack 或 Rollup)的现代 JavaScript 应用程序,捆绑器是您期望自动删除死代码的工具。 ⁣⁣

将应用程序及其依赖项视为抽象语法树(用“摇动”语法树以优化它)。
树中的每个节点都是为应用程序提供功能的依赖项。

在 Tree Shaking 中,输入文件被视为图形。
图中的每个节点都是一个顶级语句,在代码中称为“部分”。
摇树是从入口点开始的图遍历,并标记任何遍历的路径以供包含。⁣⁣⁣⁣

每个组件都可以声明符号、引用符号并依赖其他文件。
甚至“部分”都被标记为是否有副作用。
例如,语句 let firstName = ‘Jane’ 没有副作用,因为如果没有任何东西需要 foo,则可以删除该语句而没有任何观察到的差异。
但是语句 let firstName = getName() 有副作用,因为在不改变代码含义的情况下无法删除对 getName() 的调用,即使没有什么需要 firstName。


Imports

只有使用 ES2015 模块语法(导入和导出)定义的模块才能进行 tree-shaken。
导入模块的方式指定模块是否可以摇树。

摇树首先访问具有副作用的入口点文件的所有部分,然后继续遍历图的边缘,直到到达新的部分。
遍历完成后,JavaScript 包仅包含遍历期间到达的部分。
其他部分被排除在外。 ⁣⁣⁣假设我们定义了以下 utilities.js 文件:⁣⁣⁣⁣

export function read(props) {⁣⁣
    return props.book⁣⁣
}⁣⁣
⁣⁣
export function nap(props) {⁣⁣
    return props.winks⁣⁣
}

然后我们就有了下面的 index.js 文件:⁣⁣⁣⁣

import { read } from 'utilities';⁣⁣
⁣⁣
eventHandler = (e) => {⁣⁣
    read({ book: e.target.value })⁣⁣
}

⁣⁣ 在这个例子中,nap() 并不重要,因此不会包含在包中。


副作用

当我们导入一个 ES6 模块时,这个模块会立即执行。

虽然我们没有在代码中的任何地方引用模块的导出,但模块本身在执行时会影响全局范围(例如 polyfill 或全局样式表),这可能会发生。
这称为副作用。 虽然我们没有引用模块本身的导出,但如果模块开始有导出值,由于导入时的特殊行为,模块不能被摇树!

Webpack 文档对 tree-shaking 以及如何避免破坏它给出了清晰的解释。


知识点

  • tree shaking 摇树
此页面上有什么