1.概述
在webpack4中,提供mode配置选项,告知webpack使用相应模式的内置优化。
mode配置有3个参数production,development,none,默认为production。其中none很好理解,什么都没有,没有进行任何预设,而另外两个都是有预设插件使用。
我们这里就来研究下配置这些参数,都执行了什么事情。
2.none模式下的打包
在none模式下,webpack打包出来的文件(注1)如下:
// index.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],{
/***/ 1:
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__(2);
var hotClient = __webpack_require__(4);
...
/***/ }),
/***/ 111:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _app_vue_vue_type_template_id_35385eed___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(112);
...
/***/ }),
},[[0,0,2,4,3]]]);
我们可以看见webpack将模块放置到一个数组中,并给与一个id。调用某个模块,就是调用这个模块在这个数组中的id。
但是,相对于development开发环境来说,代码的可读性不够友好;对于production正式环境来说,代码不够简洁。所以,我们在development和production配置中,默认添加了一些插件来对代码进行加强。
注1: 为了便于代码阅读,我设置webpack的devtool的值为source-map,下同。
3.development模式下的打包
在官网中,对development模式的描述如下:
会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。
即等同于执行了以下的插件:
// webpack.dev.config.js
module.exports = {
+ mode: 'development'
- plugins: [
- new webpack.DefinePlugin({ "process.env.NODE_ENV": '"development"' }),
- new webpack.NamedModulesPlugin(),
- new webpack.NamedChunksPlugin(),
- ]
}
3.1 设置process.env.NODE_ENV
在mode为development的状态下,为了便于我们在项目中获取当前的模式,webpack会将全局变量process.env.NODE_ENV,并赋值为当前mode的值。等同于我们在webpack中添加插件new webpack.DefinePlugin({ "process.env.NODE_ENV": '"development"' })。
3.2 NamedModulesPlugin
当开启 HMR 的时候使用该插件会显示模块的相对路径,建议用于开发环境。
原来webpack打包时,将模块与id进行关联,新增减模块都会导致id的变化。但我们使用了插件new webpack.NamedModulesPlugin(),则将模块与其名称进行关联,这样不管新增减多少模块,模块的序号都是固定的。
// index.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[3],{
// 同上1
/***/ "./build/dev-client.js":
/***/ (function(module, exports, __webpack_require__) {
__webpack_require__("./node_modules/eventsource-polyfill/dist/browserify-eventsource.js");
...
/***/ }),
// 同上111
/***/ "./src/app.vue":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _app_vue_vue_type_template_id_35385eed___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/app.vue?vue&type=template&id=35385eed&");
...
/***/ }),
},[[0,0,4,9,2]]]);
3.3 NamedChunksPlugin
与每个模块都有一个id一样,在webpack中,每个chunk也有一个对应的id。在打包过程中,我们可以看见输出的chunk信息。
如果我们使用了runtimeChunk,我们也可以看见生成的runtime.js中关于chunk的获取逻辑:
// webpack.dev.config.js
module.exports = {
optimization: {
runtimeChunk: {
name: 'runtime',
},
}
}
// runtime.js
/******/ function jsonpScriptSrc(chunkId) {
/******/ return __webpack_require__.p + "js/" + ({"2":"vendors", "9":"main"}[chunkId]||chunkId) + ".js"
/******/ }
如果我们使用了插件new webpack.NamedChunksPlugin(),则会用chunk的名称来替换其id,实现chunkid固化。
在runtime.js中,我们也可以看见chunk的获取逻辑的改变:
/******/ function jsonpScriptSrc(chunkId) {
/******/ return __webpack_require__.p + "js/" + ({"vendors":"vendors","main":"main"}[chunkId]||chunkId) + ".js"
/******/ }
4.production模式下的打包
在官网中,对production模式的描述如下:
会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin.
即等同于执行了以下的插件:
// webpack.pord.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
+ mode: 'production'
- plugins: [
- new webpack.DefinePlugin({ "process.env.NODE_ENV": '"production"' }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin(),
- new TerserPlugin(/* ... */),
- ]
}
4.1 设置process.env.NODE_ENV
这里的逻辑同3.1,设置process.env.NODE_ENV的值为production。
4.2 OccurrenceOrderPlugin
原来名称为OccurenceOrderPlugin。webpack.optimize.occurrenceOrder用于为模块分配id,通过这个插件webpack会分析使用频率最多的模块,并为其分配最小的id,这样模块会被更快找到。
// webpack.pord.config.js
module.exports = {
optimization: {
occurrenceOrder: true
}
};
4.3 NoEmitOnErrorsPlugin
new webpack.NoEmitOnErrorsPlugin()插件可以用于防止程序报错。
在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误。
4.4 ModuleConcatenationPlugin
使用new webpack.optimize.ModuleConcatenationPlugin()可用于实现作用域提升(Scope Hoisting),让Webpack打包出来的代码文件更小、运行的更快。
4.5 SideEffectsFlagPlugin
webpack.optimization.sideEffects用于实现treeshaking形式的死码删除。而为了实现treeshaking,需要满足几个条件:
- 导入的模块已经标记了sideEffect,即package.json中的sideEffects这个属性为false。
- 当前模块引用了无副作用的模块,且没有被使用
这样,经过SideEffectsFlagPlugin处理后,没有副作用且没有被使用的模块都会被打上sideEffectFree标记。
在ModuleConcatenationPlugin中,带着sideEffectFree标记的模块将不会被打包。
// webpack.pord.config.js
module.exports = {
optimization: {
sideEffects: true
}
};
4.6 TerserPlugin
用于js代码压缩。在以前版本中,我们需要引入npm包terser-webpack-plugin来进行压缩,现在我们可以在optimize中进行配置达到同样的效果。
// webpack.pord.config.js
module.exports = {
optimization: {
minimize: true
}
}
// js压缩
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
}),
]
}
}
4.7 FlagIncludedChunksPlugin
即配置optimization.flagIncludedChunks。该配置项会使webpack确认,若当前标记的chunka是另外一个chunkA的子集并且已经A加载完成,则a将不会再次加载(包含关系)。
告知
webpack确定和标记出作为其他chunk子集的那些chunk,其方式是在已经加载过较大的chunk之后,就不再去加载这些chunk子集。optimization.flagIncludedChunks默认会在productionmode中启用,其他情况禁用。
// webpack.pord.config.js
module.exports = {
optimization: {
flagIncludedChunks: true
}
};
4.8 FlagDependencyUsagePlugin
标记没有用到的依赖。
注:还没有找到该插件的收益。