在這一部分的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。