在线文档教程

摇树优化 | Tree Shaking

Tree Shaking

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 importexport。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。

webpack 2发行版内置了对ES2015模块(别名 harmony 模块)的支持以及未使用的模块导出检测。

本指南的其余部分将来自入门。如果您尚未阅读该指南,请现在就这样做。

在我们的项目中添加一个新的通用模块文件 src/math.js,此文件导出两个函数:

项目

webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src |- index.js |- math.js |- /node_modules

src/math.js

export function square(x) { return x * x; } export function cube(x) { return x * x * x; }

接着,更新入口脚本,使用其中一个新方法,并且为了简单,将 lodash 删除:

src/index.js

- import _ from 'lodash'; + import { cube } from './math.js'; function component() { - var element = document.createElement('div' + var element = document.createElement('pre' - // Lodash, now imported by this script - element.innerHTML = _.join(['Hello', 'webpack'], ' ' + element.innerHTML = [ + 'Hello webpack!', + '5 cubed is equal to ' + cube(5) + ].join('\n\n' return element; } document.body.appendChild(component()

注意,我们__并未从 src/math.js 模块中 import 导入 square 方法__。这个功能是所谓的“未引用代码(dead code)”,也就是说,应该删除掉未被引用的 export。现在让我们运行我们的npm 脚本 npm run build,并检查输出的 bundle:

dist/bundle.js (around lines 90 - 100)

/* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* unused harmony export square */ /* harmony export (immutable) */ __webpack_exports__["a"] = cube; function square(x) { return x * x; } function cube(x) { return x * x * x; }

注意,上面的 unused harmony export square 注释。如果你看下面的代码,你会注意到 square 没有被导入,但是,它仍然被包含在 bundle 中。我们将在下一节中解决这个问题。

压缩输出

通过如上方式,我们已经可以通过 importexport 语法,找出那些需要删除的“未使用代码(dead code)”,然而,我们不只是要找出,还需要在 bundle 中删除它们。为此,我们将使用 -p(production) 这个 webpack 编译标记,来启用 uglifyjs 压缩插件。

让我们开始安装它:

npm i --save-dev uglifyjs-webpack-plugin

然后将其添加到我们的配置中:

webpack.config.js

const path = require('path' + const UglifyJSPlugin = require('uglifyjs-webpack-plugin' module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') - } + }, + plugins: [ + new UglifyJSPlugin() + ] };

注意,也可以在命令行接口中使用 --optimize-minimize 标记,来使用 UglifyJSPlugin

准备就绪后,然后运行另一个命令 npm run build,看看输出结果有没有发生改变。

你发现 dist/bundle.js 中的差异了吗?显然,现在整个 bundle 都已经被精简过,但是如果仔细观察,则不会看到 square 函数被引入,但会看到 cube 函数的修改版本(function r(e){return e*e*e}n.a=r)。现在,随着 tree shaking 和代码压缩,我们的 bundle 减小几个字节!虽然,在这个特定示例中,可能看起来没有减少很多,但是,在具有复杂的依赖树的大型应用程序上运行时,tree shaking 或许会对 bundle 产生显著的体积优化。

注意事项

请注意,webpack不会自行执行树状结构。它依赖于像UglifyJS这样的第三方工具来执行实际的死代码消除。有些情况下,树木摇晃可能无效。例如,请考虑以下模块:

transforms.js

import * as mylib from 'mylib'; export const someVar = mylib.transform{ // ... } export const someOtherVar = mylib.transform{ // ... }

index.js

import { someVar } from './transforms.js'; // Use `someVar`...

在上面的代码中,webpack无法确定调用是否mylib.transform触发任何副作用。结果,它在安全方面发生了错误,并退出someOtherVar了捆绑代码。

一般来说,当一个工具无法保证特定的代码路径不会导致副作用时,即使您确信它不应该,该代码仍会保留在生成的包中。常见情况包括调用webpack和/或缩小器无法检查的第三方模块的功能,重新导出从第三方模块导入的功能等。

本指南中使用的代码假设您使用UglifyJS插件执行树状移动。但是,还有其他工具,例如webpack-rollup-loader或Babel Minify Webpack插件,根据您的设置可能会产生不同的结果。

结论

所以,我们所学到的是,为了利用 tree shaking,你必须......

  • 使用ES2015模块语法(即importexport)。

  • 包括支持死代码删除的缩小器(例如,UglifyJSPlugin)。

您可以将您的应用程序想象为一棵树。您实际使用的源代码和库代表树的绿色活叶。死代码表示秋季消耗的棕色枯叶。为了摆脱死叶,你必须摇动树,导致它们倒下。

如果您对更多优化输出的方法感兴趣,请跳到下一个指南,了解有关构建生产的详细信息。