在这一部分的webpack 4 教程中,咱们继续进一步考虑优化。咱们要学习“tree shaking”是啥以及如何使用。你会学到在webpack 4中使用“tree shaking”技术有什么要求,它能带来什么好处。开始吧!html
首先,咱们回答“tree shaking”是啥技术以及它能带来什么好处。咱们经常碰到这样的案例,须要从某文件中命名导出(某一个或几个变量、函数、对象等),然而这个文件还有许多其它(咱们此次并不须要)的导出,webpack会无论三七二十一简单粗暴的将整个模块包含进来,使得咱们最终打包的文件里有了许多不须要的垃圾。这就到了tree shaking出手的地方了,由于它能帮助咱们干掉那些死代码,大大减小打包的尺寸。node
若是你想对imports和exports了解更多,请查阅本教程1。
要想让tree shaking能“摇起来”,有几个要求,首先,必须使用ES6模块,不能使用其它类型的模块如CommonJS之流。若是使用Babel的话,这里有一个小问题,由于Babel的预案(preset)默认会将任何模块类型都转译成CommonJS类型。修正这个问题也很简单,不是在.babelrc文件中就是在webpack.config.js文件中设置modules: false就行了。webpack
// .babelrc { "presets": [ ["env", { "modules": false } ] ] }
// webpack.config.js module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, use: { loader: 'babel-loader', options: { presets: ['env', { modules: false }] } } } ] },
若是你想阅读更多有关babel-loader或loaders的通常用法,请看本教程之2
第二个要求,须要使用UglifyJsPlugin插件。若是在mode:"production"模式,这个插件已经默认添加了,若是在其它模式下,能够手工添加它。web
不熟悉UglifyJsPlugin插件的话,请参阅本教程5
另外要记住的是打开optimization.usedExports。在mode: "production"模式下,它也是默认打开了的。它告诉webpack每一个模块明确使用exports。这样以后,webpack会在打包文件中添加诸如/* unused harmony export */
这样的注释,其后UglifyJsPlugin插件会对这些注释做出理解。json
Harmony 是 ES6 和 ES2015的代号。
咱们来看看下面的情形:babel
// 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));
配置不恰当的运行就会有以下的输出:ide
/*(...)*/ /* 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没有“摇”咱们的包!这里既有add函数又有subtract函数。再使用以下的配置咱们来体验一点点不一样:函数
// 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插件的大部分选项配置以便更清楚的观察代码中发生了什么。运行以后有下面的输出:学习
/* (...) */ /* 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)); /***/} /******/ ]);
正由于optimization.usedExports和UglifyJsPlugin插件关掉的配置项,垃圾代码清除了。请注意,这是UglifyJsPlugin插件的默认行为,因此默认配置下使用UglifyJsPlugin也将清除死代码(除非运行了其它的压缩进程)。优化
若是你想要对库进行tree shake,首先要记住的注意点仍是前面所说的:使用ES6模块。然而许多库并不必定使用ES6模块,典型的例子好比lodash就是这样,看它的源代码,很明显它没有使用ES6模块。
假设要使用lodash库中的debounce函数。
// index.js import _ from 'lodash'; console.log(_.debounce);
如今你的输出里把整个lodash都打进来了。当使用import _ from 'lodash'时没有办法避免这一点。还好,有人建立了一个叫lodash-es的库,它改写lodash按ES6模块的形式导出。
import { debounce } from 'lodash';// (译注:原文如此,应该为'lodash-es') console.log(debounce);
很不幸,webpack依旧没有对其tree shake。根据ECMAScript规范,全部的子模块都须要去评估,由于它们可能有反作用。我推荐读一下Sean Larking(他是webpack核心团队成员之一)在Stack Overflow上写的这篇很好的解释。库的做者能够在package.json文件里注明它的库没有反作用。打开lodash库的package.json文件,能够看到有"sideEffects": false的标注。那么这儿的问题是什么?
Webpack默认忽略了sideEffect标注,改变此行为须要设置optimization.sideEffects为true。你能手工设置它或经过设置mode:"production"模式也行。
// 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() ] }
如今webpack终于对lodash库“摇”起来了。
要让tree shaking好好工做,有必定条件。这么颇有用的功能,固然值得学习。但愿经过这篇文章,你会知道怎么去使用它,由于它能大大减小你打包的体积。记住须要使用ES6模块和UglifyJsPlugin插件,以及配置optimization选项,设置usedExports和sideEffects为true。