Webpack 4 教程 - 7. 經過「tree shaking」減小打包的尺寸

在這一部分的webpack 4 教程中,咱們繼續進一步考慮優化。咱們要學習「tree shaking」是啥以及如何使用。你會學到在webpack 4中使用「tree shaking」技術有什麼要求,它能帶來什麼好處。開始吧!html

Webpack 4 Tree Shaking

首先,咱們回答「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 Shaking

若是你想要對庫進行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。

相關文章
相關標籤/搜索