性能模式
摇树优化
可能会出现这种情况,我们在捆绑包中添加了在我们的应用程序中任何地方都没有使用的代码。 这些无用代码可以被消除,以减少捆绑包的大小,并防止不必要地加载更多数据! 在将无用代码添加到捆绑包之前将其消除的过程称为摇树优化。
虽然摇树优化对像 math
模块这样的简单模块有效,但有些情况下摇树优化会很棘手。
概念
摇树优化的目标是从最终的 JavaScript 捆绑包中删除永远不会使用的代码。 如果操作正确,它可以减小 JavaScript 捆绑包的大小,并缩短下载、解析以及(在某些情况下)执行时间。 对于大多数使用模块捆绑器(如 webpack 或 Rollup)的现代 JavaScript 应用程序,您的捆绑器应该能够自动删除无用代码。
将您的应用程序及其依赖项视为一个抽象语法树(我们希望“摇动”语法树以优化它)。 树中的每个节点都是一个依赖项,它为您的应用程序提供功能。 在摇树优化中,输入文件被视为一个图。 图中的每个节点都是一个顶级语句,在代码中称为“部分”。 摇树优化是一种图遍历,它从入口点开始,并标记任何遍历的路径以供包含。
每个组件都可以声明符号、引用符号并依赖于其他文件。 即使“部分”也被标记为具有副作用或没有副作用。 例如,语句 let firstName = 'Jane'
没有副作用,因为如果不需要 firstName,则可以删除该语句,没有任何观察到的差异。 但是语句 let firstName = getName()
具有副作用,因为调用 getName()
不能删除,而不会改变代码的含义,即使不需要 firstName。
导入
只有使用 ES2015 模块语法(import
和 export
)定义的模块才能被摇树优化。 您导入模块的方式指定了模块是否可以被摇树优化。
摇树优化从访问具有副作用的入口点文件的所有部分开始,并继续遍历图的边,直到到达新的部分。 一旦遍历完成,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 文档提供了关于摇树优化的清晰的解释 以及如何避免破坏它。