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。

相关文章
相关标签/搜索