webpack4支持的一个新特性就是zero配置,不须要config,也能够打包,这对于懒癌患者颇有诱惑力,可是这也意味着咱们不清楚零配置发生了写什么,也不知道打包出来的文件是否符合咱们的心意,所有都是佛系打包。不过做为项目的亲爹亲妈,仍是要对本身的孩子负责,每一个打包过程都是要可控的。本文就是详解不一样mode下,webpack打包都发生了些什么事。webpack
咱们来看一下MODE这个参数,他有三个参数production
,development
,none
,前两个是有预设的插件,而最后一个则是什么都没有,也就是说设置为none
的话,webpack就是最初的样子,无任何预设,须要从无到有开始配置。git
在webpack的配置中,其余配置均可以没有!可是mode是必备的,若是不加mode,官方虽然会打包,但同时也会给你一个警告:github
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/web
意思很简单,就是mode没有被设置的状况下,系统就会给你一个默认的production
模式。json
mode配置很简单,就只有3个值,任君挑选。none
这个参数,相信你们都能理解,那么咱们就研究下其余两个production
和development
,这为何要有这两个状态,以及二者在webpack打包中都干了些啥事。数组
production
和development
的状态在mode为production
或development
的状态下,为了兼顾两个状态下的程序运行,webpack建立了一个全局变量process.env.NODE_ENV
,等同于在插件plugins中加入了new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development|production") })
,用来区分不一样的状态,同时能够在程序中区分程序状态。jsp
那么咱们该如何在coding的时候进行区分呢?由于process.env.NODE_ENV
是全局变脸给,因此能够这样来引用值,假设mode:production
:ide
if ("development" === process.env.NODE_ENV){ .... }else{ .... }
编译以后:优化
if ("development" === "production"){ .... }else{ .... }
也就是最后process.env.NODE_ENV会被替换为一个常量。这个小功能能够帮助咱们在写业务JS的时候,区分线上版本与开发版本。ui
none
模式下的模块打包在没有任何优化处理的状况下,按照webpack默认的状况下打包出来的模块是怎么样的呢?下方是一个简易的例子,咱们能够看出,他将模块打包至数组之中,调用模块的时候,就是直接调用模块在此数组中的一个序号。而后没有压缩混淆之类的优化,连注释都帮咱们标的好好的,好比导入 /* harmony import / ,/ harmony default export */。
[ /* 0 */ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _page2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); console.log(_page2_js__WEBPACK_IMPORTED_MODULE_0__["default"]) }), /* 1 */ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); let str="page1" /* harmony default export */ __webpack_exports__["default"] = (str); }) ]
可是不管是在开发环境development下,仍是在正式坏境production下,这个代码都是不过关的,对于开发环境,此代码可读性太差,对于正式环境,此代码不够简洁,所以,为了减小一些重复操做,webpack4提供的development|production能够很大程度上帮咱们作掉一大部分事,咱们要作的就是在这些事的基础上加功能。
development
是告诉程序,我如今是开发状态,也就是打包出来的内容要对开发友好。在此mode下,就作了如下插件的事,其余都没作,因此这些插件能够省略。
// webpack.development.config.js module.exports = { + mode: 'development' - devtool: 'eval', - plugins: [ - new webpack.NamedModulesPlugin(), - new webpack.NamedChunksPlugin(), - new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }), - ] }
咱们看看NamedModulesPlugin
和NamedChunksPlugin
这两个插件都作了啥,本来咱们的webpack并不会给打包的模块加上姓名,通常都是按照序号来,从0开始,而后加载第几个模块。这个对机器来讲无所谓,查找载入很快,可是对于人脑来讲就是灾难了,因此这个时候给各个模块加上姓名,便于开发的时候查找。
没有NamedModulesPlugin
,模块就是一个数组,引用也是按照在数组中的顺序引用,新增减模块都会致使序号的变化,就是webpack默认打包下的状况,参考上一节。
有了NamedModulesPlugin
,模块都拥有了姓名,并且都是独一无二的key,无论新增减多少模块,模块的key都是固定的。
{ "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); var _page2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/page2.js"); console.log(_page2_js__WEBPACK_IMPORTED_MODULE_0__["default"]) }), "./src/page2.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); let str="page1" __webpack_exports__["default"] = (str); }) }
除了NamedModulesPlugin
,还有一个NamedChunksPlugin
,这个是给配置的每一个chunks命名,本来的chunks也是数组,没有姓名。
Asset Size Chunks Chunk Names index.js 4.04 KiB 0 [emitted] index page2.js 3.75 KiB 1 [emitted] page2
Asset Size Chunks Chunk Names index.js 4.1 KiB index [emitted] index page1.js 4.15 KiB page1 [emitted] page1
NamedChunksPlugin
其实就提供了一个功能就是你能够自定义chunks的名字,假如我再不一样的包中有相同chunk名,怎么办?这个时候就要在进行区分了,我么能够用全部的依赖模块名加本上的模块名。由于Chunk.modules
已经废弃了,如今用其余的方法来代替chunk.mapModules
,而后重命名chunk的名字:
new webpack.NamedChunksPlugin((chunk) => { return chunk.mapModules(m => { return path.relative(m.context, m.request) }).join("_") }),
看一眼作这一行代码的效果,咱们能够看到Chunks这边已经重命名了,这样能够很大程度上解决chunks重名的问题:
Asset Size Chunks Chunk Names index.js 4.1 KiB index.js_page2.js [emitted] index page2.js 3.78 KiB page2.js [emitted] page2
总结:development也就给咱们省略了命名的过程,其余的仍是要本身加的。
在正式版本中,所省略的插件们,以下所示,咱们会一个个分析。
// webpack.production.config.js module.exports = { + mode: 'production', - plugins: [ - new UglifyJsPlugin(/* ... */), - new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ] }
咱们第一个须要处理的就要混淆&压缩JS了吧,这个时候就要请出UglifyJs
了,在webpack中他的名字是const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
,这样就可使用他了。
不过new UglifyJsPlugin()
,这个插件咱们能够在optimize
中配置,效果是同样的,那么咱们是否是就不用再导入一个新的插件了,这样反而会拖慢webpack的就打包速度。
optimization:{ minimize: true, },
将插件去除,混淆压缩放入optimization
,这样webpack速度快的飞起了。只有第一次打包会慢,以后再打包就快了。
webpack.optimize.ModuleConcatenationPlugin()
这个插件的做用是什么呢?官方文档上是这么描述的:
记住,此插件仅适用于由 webpack 直接处理的 ES6 模块。在使用转译器(transpiler)时,你须要禁用对模块的处理(例如 Babel 中的 modules 选项)。
最后一个插件就是webpack.NoEmitOnErrorsPlugin()
,这个就是用于防止程序报错,就算有错误也给我继续编译,很暴力的作法呢。
还有一些默认的插件配置,也就是能够不在plugins中引用的配置:
flagIncludedChunks
这个配置的做用是,看结果:
未启用
Asset Size Chunks Chunk Names index.js 1.02 KiB 0 [emitted] index page1.js 970 bytes 1 [emitted] page1
启用后,若是只有二个文件彷佛表现不明显,因而我增长了三个文件,page1调用page2,index调用page1,那么一目了然,在这里的chunks就是全部引用模块的id。
Asset Size Chunks Chunk Names index.js 1.08 KiB 0, 1, 2 [emitted] index page1.js 1.01 KiB 1, 2 [emitted] page1 page2.js 971 bytes 2 [emitted] page2
webpack.optimize.OccurrenceOrderPlugin
这个插件的做用是按照chunk引用次数来安排出现顺序,由于这让常常引用的模块和chunk拥有更小的id。将上面的例子加上这个配置运行下就是这样的。
Asset Size Chunks Chunk Names page2.js 969 bytes 0 [emitted] page2 page1.js 1.01 KiB 1, 0 [emitted] page1 index.js 1.08 KiB 2, 0, 1 [emitted] index
webpack.optimize.SideEffectsFlagPlugin()
这个插件若是须要生效的话,须要两个条件,一个是导入的模块已经标记了sideEffect,即package.json中的sideEffects这个属性为false,第二个就是当前模块引用了次无反作用的模块,并且没有使用。那么在打包的时候,就不会将这个模块打包到文件中。
实际上production mode下,与官方文档相比,他的配置更加等同于以下配置:
module.exports = { mode:"none", optimization:{ flagIncludedChunks:true, minimize: true, }, plugins: [ new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), new webpack.optimize.ModuleConcatenationPlugin(), new webpack.NoEmitOnErrorsPlugin(), new webpack.optimize.SideEffectsFlagPlugin() ] }
name | effect |
---|---|
FlagDependencyUsagePlugin | 标记没有用到的依赖,这个插件没法经过webpack获取,我只能经过强行导入webpack/lib下的class文件来导入。 |
SideEffectsFlagPlugin | 用于处理tree shaking的,tree shaking,sideEffect这个插件的做用就是,若是当前的模块没有引用,并且package.json中的sideEffects为false,那么打包的时候就能够将此包剔除。stackoverflow上有用的答案 |
FlagIncludedChunksPlugin | 给当前chunk包含的chunkid加入chunk名之中 |
ModuleConcatenationPlugin | 做用域提高 |
NoEmitOnErrorsPlugin | 阻止任何报错 |
OccurrenceOrderPlugin | 按照调用次数来给chunks排序 |
UglifyJsPlugin | 混淆压缩 |