在本次Webpack 4教程中,我们会更进一步讲述项目优化。我们会学习什么是tree shaking以及如何使用它。你会找到让Webpack 4中tree shaking运作起来所需要的东西,并知道怎样从中受益。开始吧!
首先,让我们来回答什么是tree shaking以及它带来什么好处。我们常常在文件中使用具名引入(named imports),这些引入的文件里有其他导出(exports)。在某些情况下,我们并没有引入所有的导出,但Webpack仍会把整个模块都导入进来。这种情况下就需要使用tree shaking了,因为它能帮助我们去除掉用不到的代码。因此打包后的体积能显著下降。
为了让tree shaking起作用,你需要满足一些配置要求。首先,必须使用ES 6模块,而不是使用诸如CommonJS的模块处理方式。如果你在使用Babel,这一点可能已让你遇到麻烦了。因为Babel的预置默认把任何模块转译成CommonJS模块。你可以简单设置modules: false来解决此问题,在.babalrc或者webpack.config.js中设置都可以。
代码语言:javascript复制.babelrc
{
"presets": [
["env",
{
"modules": false
}
]
]
}
// webpack.config.js
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', { modules: false }]
}
}
}
]
},
你需要使用UglifyJsPlugin。默认情况下,它在mode: "produnction"是被启用。如果你倾向于不使用mode: "produnction",你可以手动添加UglifyJsPlugin。
还有一件记得做的事情是,你需要打开optimization.usedExports。它同样在mode: "produnction"时被默认添加上去了。它告诉Webpack去决定每一个模块所用到的导出。有了它,Webpack会在你的打包产出里添加额外的像是/* unused harmony export */之类的注释,UglifyJsPlugin在之后会使用到它们。
Harmony是ES6和ES2015的代号。
让我们来研究有关例子。
代码语言:javascript复制// utilities.js
export function add(a, b) {
return a b;
}
export function subtract(a, b) {
return a - b;
}
// index.js
import { add } from './utilities';
console.log(add(1,2));
console.log(add(3,4));
以正常配置运行Webpack,我们得到像下面这样的输出:
代码语言:javascript复制/*(...)*/
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "add", function() { return add; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "substract", function() { return substract; });
function add(a, b) {
return a b;
}
function subtract(a, b) {
return a - b;
}
/***/ })
/******/ ]);
正如你看到的,Webpack没有对我们的打包输出进行tree-shaking。这里同时有add和subtract方法。我们来试验一下,使用下面的配置:
代码语言:javascript复制// webpack.config.js
const webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const UglifyJS = require('uglify-es');
const DefaultUglifyJsOptions = UglifyJS.default_options();
const compress = DefaultUglifyJsOptions.compress;
for(let compressOption in compress) {
compress[compressOption] = false;
}
compress.unused = true;
module.exports = {
mode: 'none',
optimization: {
minimize: true,
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress,
mangle: false,
output: {
beautify: true
}
},
})
],
}
}
我已经关掉了大部分UglifyJsPlugin的配置,以便于我们清楚地看到我们的代码发生了什么。使用上面的配置运行,得到下面输出:
代码语言:javascript复制/* (...) */
/* 0 */
/***/ function() {
"use strict";
// CONCATENATED MODULE: ./src/utilities.js
function add(a, b) {
return a b;
}
// CONCATENATED MODULE: ./src/index.js
console.log(add(1, 2));
console.log(add(3, 4));
/***/}
/******/ ]);
由于使用了UglifyJsPlugin的optimization.usedExports和unused选项,不需要的代码被移除了。请注意,那是UglifyJsPlugin的默认行为,所以使用默认配置也能去除无用的代码(当然这样还会进行其他压缩处理)。
Tree shaking函数库
如果你打算对函数库进行tree shaking,你需要记得上一段提到的是事情:使用ES6模块,而它并不是总是被函数库使用。一个绝佳的例子是lodash。如果你去看它提供的产品代码,可以清楚地看到它并没有使用ES6模块。
试想我们打算使用lodash提供的debounce方法。
代码语言:javascript复制// index.js
import _ from 'lodash';
console.log(_.debounce);
现在你的输出里包含了整个lodash库。当使用import _ from 'lodash'时,这无法避免。但不要担心!有人已经思考过此问题,并创建了一个包叫做lodash-es。它以ES6模块的形式提供了lodash库。
代码语言:javascript复制import { debounce } from 'lodash';
console.log(debounce);
Webpac默认会忽略sideEffect标识。如果想改变这种行为,我们需要吧optimization.sideEffects设置成true。你可以手动设置,或者通过mode: "produnction"实现。
代码语言:javascript复制// webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'none',
optimization: {
minimize: true,
minimizer: [
new UglifyJsPlugin()
],
usedExports: true,
sideEffects: true
},
plugins: [
new HtmlWebpackPlugin()
]
}
现在lodash库能够被Webpack进行tree shaking了。
总结
为了使tree shaking起作用,需要满足许多条件。它是个很有用的特性,当然也值得学习。希望你通过本文了解如何使用它,让打包后的体积大幅减小。记住你需要使用ES6模块和UglifyJsPlugin。另外,记得配置optimization,把usedExports和sideEffects设为true。