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: webpack.js.org/concepts/mo…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") })
,用來區分不一樣的狀態,同時能夠在程序中區分程序狀態。bash
那麼咱們該如何在coding的時候進行區分呢?由於process.env.NODE_ENV
是全局變臉給,因此能夠這樣來引用值,假設mode:production
:jsp
if ("development" === process.env.NODE_ENV){
....
}else{
....
}
複製代碼
編譯以後:ide
if ("development" === "production"){
....
}else{
....
}
複製代碼
也就是最後process.env.NODE_ENV會被替換爲一個常量。這個小功能能夠幫助咱們在寫業務JS的時候,區分線上版本與開發版本。優化
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 | 混淆壓縮 |