Webpack 4 教程 - 5. 生产模式下内建的优化

tianyin 2019-06-28

在本次教程中,我们聚焦于如何在你的应用中减少输出文件的体积,从而提升用户体验。这意味着在生产环境下要用不同的方式来处理代码。今天我们将阐述webpack通过mode参数来设置其内置的优化措施, 开始吧。

Webpack 4教程:为何优化代码

首先,我们回答究竟为什么要优化你的代码这个问题。如果你有良好编程实践,你可能注重代码的可读性,因此你在代码中添加了大量的空白符(制表符、空格、换行符)和注释。在代码变得更漂亮的同时,也使得文件的体积大大增加了。另一方面,为了用户体验(指减少文件体积)而牺牲可读性也不可取,手工这么做的话很繁琐。因此,这儿有一种解决方案供你在项目中选择。

Mode: production

Webpack4中引入了一个新参数:mode。要求总是在配置中指定。如果不指定,会产生一个警告并退而其次的使用默认值,默认值就是production。如果你使用 mode:"production", Webpack将配置成生成更适合在生产环境下的代码。我们现在就来看看webpack为我们确切地做了些什么。

UglifyJsPlugin插件

将mode值设为production将在配置中添加UglifyJsPlugin插件,它通过压缩和最小化,使得你的代码更短,运行得更快。其任务包括简单的缩短变量名,移除空白符,删除重复代码等等。默认会解析每一个.js文件。这篇博文中,我们将讲述UglifyJSPlugin这个插件最基本的配置。即使webpack 4 依据选择的mode值进行了优化,你仍能通过optimization属性来进行自己的配置。

// webpack.config.js

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    mode: "production",
    // using mode: "production" attaches the following configuration:
    optimization: {
        minimize: true,
        minimizer: [
            new UglifyJsPlugin()
        ]
    },
}

传给UglifyJsPlugins插件最要紧的属性是uglifyOptions,它有大量默认的配置。而其中最值得关注的部分是compress属性。

new UglifyJsPlugin({
    uglifyOptions: {
        compress: {
            /*(...)*/
        }
    }
})

它负责UglifyJsPlugin插件的很多重要的举措,从而是你的代码更短,更轻。完整列表请参阅官方文档,其也标记出了默认值。

UglifyJsPlugin插件另外一个重要的属性是output。

new UglifyJsPlugin({
    uglifyOptions: {
        compress: {
            /*(...)*/
        },
        output: {
            /*(...)*/
        }
    }
})

默认,代码生成器试图输出最短的代码。你可以通过更改output的配置来改变这一行为。你可能无需对默认值改动太多,但有一个值得考虑的属性:drop_console,默认它的设置值是false。如果改为true,将删除代码中所有的console.log调用。如果想了解更多output的属性,请查阅完整列表

UglifyJsPlugin还有更多的可能的配置,请参读它在Github上的文档

DefinePlugin插件

这个插件允许你创建全局常量用于编译时解析。如果设置mode:"production",webpack默认会设置"process.env.NODE_ENV": JSON.stringify("production")。

// webpack.config.js

module.exports = {
    mode: "production",
    // using mode: "production" attaches the following configuration:
    plugins: [
        new webpack.DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify("production")
        }),
    ]
}

注意因为直接文本替换,所给的属性值必须包括引号,要这么做JSON.stringify("production")或'"production"'。

在编译时解析意味着你在代码中使用的process.env.NODE_ENV,将被替换成production这个值。

console.log(process.env.NODE_ENV);
if(process.env.NODE_ENV === 'production') {
    console.log('this is production!')
}

记住,在webpack编译完代码之后就没有process.env.NODE_ENV这个常量值了。上面的代码在webpack处理之后变成下面的样子:

console.log("production");
if(true) {
    console.log("this is production!")
}

在UglifyJSPlugin插件最小化处理之后,它更是简化为:

console.log("production");
console.log("this is production!")

NoEmitOnErrorsPlugin插件

使用这个插件将赞助你处理编译期间的错误。例如,可能出现你试图导入一个webpack不能解析(译注:就是找不到啦)的文件的情形。此时,Webpack会创建一个有错误信息的新版应用。如果使用NoEmitOnErrorsPlugin,就根本不会创建这个版本。
(译注:原文是这样子的:
Using this plugin will help you deal with errors during the compilation. For example, there might be a situation in which you try to import a file that Webpack can’t resolve. In this situation, Webpack creates a new version of the application with the information about the error. With the usage of NoEmitOnErrorsPlugin, this version is not created at all.
是不是说得云里雾里?其实意思是,如果没有使用NoEmitOnErrorsPlugin,当发生错误时,就会重载一个有错误信息的页面,把用户的屏幕搞花。使用NoEmitOnErrorsPlugin插件,就不会加载这个页面了,错误信息只是在控制台中输出)

// webpack.config.js

const webpack = require('webpack');

module.exports = {
    mode: "production",
    // using mode: "production" attaches the following configuration:
    plugins: [
        new webpack.NoEmitOnErrorsPlugin();
    ]
}

ModuleConcatenationPlugin插件

Webpack默认将每一个模块包装在独立的闭包函数中,这个包装函数使得javascript的执行稍微变慢了一点。看看下面的例子:

// one.js

const dog = 'Fluffy';
export const one = 1;
// two.js

const dog = 'Fluffy';
export const two = 2;
// index.js

import { one } from './one';
import { two } from './two';
const dog = 'Fluffy';

console.log(one, two);

如没有ModuleConcatenationPlugin插件,输出的打包象这个样子:

// main.js

(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _one__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var _two__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);


const dog = 'Fluffy';

console.log(_one__WEBPACK_IMPORTED_MODULE_0__["one"], _two__WEBPACK_IMPORTED_MODULE_1__["two"]);

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "one", function() { return one; });
const dog = 'Fluffy';
const one = 1;



/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "two", function() { return two; });
const dog = 'Fluffy';
const two = 2;

/***/ })
/******/ ]);

当设置mode为production,ModuleConcatenationPlugin插件就会尽心尽责。谢天谢地,现在输出的打包在一个作用域里了。更少的函数意味着更少的运行时开销。

注意,这个例子中我没有进行任何缩小化。由于缩小化器现在知道了模块间的依赖,它能更好的干活。

// main.js

(function(module, __webpack_exports__, __webpack_require__) {

"use strict";

// CONCATENATED MODULE: ./src/one.js
const dog = 'Fluffy';
const one = 1;


// CONCATENATED MODULE: ./src/two.js
const two_dog = 'Fluffy';
const two = 2;
// CONCATENATED MODULE: ./src/index.js


const src_dog = 'Fluffy';

console.log(one, two);

/***/ })
/******/ ]);

如果你觉得有趣,请阅读article on the webpack blog上针对这一特性的博文。

小结

今天我们学习了配置mode:'production'时Webpack能做的内置优化措施。这样使得你的应用加载得更快,执行得更好。经过一系列的配置处理,达到如此目标,从而满足产品需要。下一个教程我们将覆盖开发模式下的配置,敬请期待!

相关推荐