webpack 可以看做是模塊打包機。它做的事情是:分析你的項目結構,找到JavaScript模塊以及其它的一些瀏覽器不能直接運行的拓展語言(Scss,TypeScript等),並將其打包為合適的格式以供瀏覽器使用。目前,webpack 總共發布了三個穩定版本。從17年八月底開始,經歷了長達五個月的開發周期,webpack 團隊通過增加大量新特性、bug修復、問題改善並於近期發布了 webpack 4.0.0 的 beta 版本。如果你對 webpack 感興趣,下面我們就來學習一下 webpack 4.0.0-beta.0 的新特性。
P.S. 以下所有代碼演示代碼都是基於 webpack 4.0.0-beta.0。
1、安裝webpack v4.0.0-beta.0
如果你使用yarn:
yarn add webpack@next webpack-cli --dev
如果你使用npm:
npm install webpack@next webpack-cli --save-dev
2、webpack 4.0.0.beta.0 新特性介紹
下面是一些你肯定會感興趣的新特性。如果閱讀完本章后還覺得不過癮,你可以再這查看完整的 changelog 。
本章將從以下幾部分來介紹 webpack 4.0.0-beta.0。
2.1 環境
webpack 運行環境升級。已經不支持 Node.js 4 版本。源碼升級到更高的 ECMAScript 版本。
根據 webpack package.json 配置中顯示 Node.js 最低支持版本:”node”: “>=6.11.5”
2.2 模塊
webpack 模塊類型及 .mjs 的支持:
長久以來,JS是webapck中唯一的模塊類型。正因此,開發者無法有效地打包其它類型的文件。目前,webpack實現了五種模塊類型,它們各有自己的優勢,可按需要使用(后面會詳細說明)。
javascript/auto
: (webpack3中默認)支持所有的JS模塊系統:CommonJS、AMD、ESM。javascript/esm
: EcmaScript模塊,所有其他模塊系統不可用(.mjs文件中默認)。javascript/dynamic
: 不支持CommonJS和EcmaScript模塊。json
: JSON數據,可以通過require和import導入(.json文件默認)。webassembly/experimental
: WebAssembly模式(目前處於實驗性階段,.wasm文件默認)。
用法:
module.rules 中的 type 就是新增加的屬性,用來支持不同的模塊類型。
module: { rules: [{ test: /\.special\.json$/, type: "javascript/auto", use: "special-loader" }] }
此外,現在webpack 按照 .wasm, .mjs, .js, 以及 .json 等擴展名的順序來解析。
javascript/esm
相比於 javascript/auto
處理ESM更加嚴格:
具體表現在兩個方面:1. 導入的名稱必須存在於導入的模塊中。2. 動態的模塊(非ESM,例如CommonJS)只能通過默認 import 導入,其他所有(包括命名空間導入)的導入都會報錯。
2.3 用法
- 必須在 “開發或者生產” 中選擇一種模式(這里有一種隱藏模式 none,可以禁用一切功能)。
1)生產模式不支持監聽,開發模式針對快速增量重建進行了優化。
2)生產模式同樣支持模塊串聯,即變量提升(此功能在webpack 3 中已經實現)。
3)開發模式下支持注釋和提示,並且支持 eval 的source map。 - 將 CLI 移動到 webpack-cli 中,你需要通過安裝 webpack-cli 使用 CLI。
- 你可以使用 optimization.* 標志來配置自己的自定義模式。
- webpackInclude 和 webpackExclude 可以通過神奇的注釋來支持 import() ,他們允許在使用動態表達式時過濾文件。
- 使用 System.import() 會發出警告:
1)可以使用 Rule.parser.system:true 關閉警告。
2)你也可以使用 Rule.parser.system:false 關閉 System.import()。 - 對於遷移到新的插件系統的插件 ProgressPlugin 現在顯示插件名稱。
- webpack 現在可以本地處理 JSON。如果用 loader 轉換 json 為 js,需要設置: type:”javascript/auto”。當然,不用 loader webpack 依然可以正常工作。
2.4 配置
- 刪除了一些常用內置插件:
1)NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors (生產模式默認)。
2)ModuleConcatenationPlugin -> optimization.concatenateModules (生產模式默認)。
3)NamedModulesPlugin -> optimization.namedModules (開發模式默認)。
刪除了常用的 CommonsChunkPlugin -> optimization.splitChunks對於那些需要細粒度控制緩存策略的人,可以通過 optimization.splitChunks和 optimization.runtimeChunk。 現在可以使用 module.rules[].resolve來配置解析。它與全局配置合並。 - optimization.minimize 用於控制minimizing的開關。 生產模式默認為開,開發模式默認為關。
- optimization.minimizer 用於配置minimizers和選項。
- 許多支持占位符的配置選項現在也支持函數形式。
- 錯誤的 options.dependencies 配置現在會拋出異常。
- sideEffects 可以通過 module.rules 覆蓋。
- 添加 output.globalObject 配置選項以允許在運行時選擇全局對象引用。
- 無需顯式設置entry和output屬性,webpack默認設置entry屬性為./src,output的屬性為./dist。
- 移除module.loaders。
2.5 優化
- uglifyjs-webpack-plugin 升級到了 v 1,並且支持 ES6語法。
- 可以在 package.json 中配置 sideEffects:false 。當設置這個字段之后,標識在使用的庫里沒有任何副作用。這意味着webpack可以從代碼中安全地清除任何re-exports。
- 使用JSONP數組來代替JSONP函數 –> 異步支持。
- 引入新的 optimization.splitChunks 選項。
- webpack 可以刪除無用代碼,之前是由 Uglify 刪除無用的代碼,現在 webpack 也可以刪除無用的代碼。這可以有效防止在 import 無用的代碼之后發生的崩潰。
以下是一些內部優化:
1)用 tap 調用替換 plugin 調用(新的插件系統)。
2)將許多廢棄的插件遷移到新的插件系統API。
3)為 json 模塊添加 buildMeta.exportsType:default。
4)刪除了 Parser (parserStringArray, parserCalculatedStringArray) 中未使用的方法。
2.6 性能
- 默認情況,UglifyJS 默認緩存和並行化(並未完全實現緩存和並行化,webpack5的里程碑)。
- 發布了一個新版本的插件系統,所以事件鈎子和處理程序變的單一化。
- 多個性能改進,特別是更快的增量重建。
- 改進了RemoveParentModluesPlugin的性能。
2.7 不兼容的改變(插件、loader相關)
- 新的插件系統:
1)插件方法是向后兼容的
2)插件現在應該這樣使用Compiler.hooks.xxx.tap(<plugin name>, fn)
Chunk.chunks/parents/blocks
不再是數組。在內部使用一個集合,並且有方法來訪問它。Parser.scope.renames
和Parser.scope.definitions
不再是對象/數組,而是Map/Set。- 解析器使用
StackedSetMap
(類似於LevelDB的數據結構)而不是數組。 - 在應用插件時不再設置
Compiler.options
。 - 所有模塊的構造參數都發生了變化。
- 將
options
合並到ContextModule
和resolveDependencies
的options
對象中. - 更改並重命名
import()
的依賴關系 - 將
Compiler.resolvers
移入可通過插件訪問的Compiler.resolverFactory
中。 Dependency.isEqualResource
已被替換為Dependency.getResourceIdentifier
Template
方法都是靜態的。- 已經添加了一個新的
RuntimeTemplate
類,outputOptions
和requestShortener
已經被移動到這個類中。
1)已經更新了許多方法來代替RuntimeTemplate
的使用。
2)我們計划將訪問運行時的代碼移動到這個新類中 - Module.meta已被Module.buildMeta所取代
- 已添加Module.buildInfo和Module.factoryMeta
- Module的一些屬性已經被移動到新的對象中
- 添加指向上下文選項的
loaderContext.rootContext
。loaders
可以使用它來創建相對於應用程序根目錄的東西。 - 當啟用HMR時,將
this.hot
標志添加到 loader 上下文中。 buildMeta.harmony
已被替換為buildMeta.exportsType:namespace
。- chunk 圖已經改變:
之前:Chunks 的連接與嵌套依賴關系有關。
現在:ChunksGroups 的連接與引用依賴有關,按照順序串聯。
之前:AsyncDependenciesBlocks 按順序引用 Chunks 列表。
現在:AsyncDependenciesBlocks 引用一個 ChunkGroup。
★★ 注意:以上內容都是關於 loaders、plugins 重大的變化。
3、重點更新詳解
3.1 更好的默認值
直到今日,webpack 總是要求顯式地設置 entry
和 output
屬性。webpack 4.0.0-beta.0 中,webpack 會自動設定你的 entry
屬性為 ./src
以及output
的屬性為 ./dist
。
這意味着您 不再需要配置文件 來啟動 webpack。接下來我們為你演示webpack 4.0.0-beta.0的便捷操作:
1、我們需要安裝好 webpack 之后,在 package.json 中添加如下腳本即可啟動:
"scripts": { "build": "webpack" },
2、在工程中添加簡單示例代碼如下圖(整個工程沒有 webpack 配置文件,即可運行打包):
3、打包過程中我們發現有新特性的提示:
WARNING in configuration The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.
這就是我們下節要說的內容 模式設置 。
★★ 注意:入口默認為 ./src
如果缺少此文件夾會報錯!
> webpack --mode production ERROR in Entry module not found: Error: Can't resolve './src' in 'D:\workspace\github\Webpack-Example'
3.2 模式設置
以往的項目使用 webpack3 腳手架生成項目初始模板都會有兩個甚至三個配置文件,比如 webpack.base.conf.js
、 webpack.prod.conf.js
、 webpack.dev.conf.js
而現在可以做到一個配置文件都不需要,直接在啟動命令中傳入參數--mode development | production
達到區分不同模式的效果。
接下來修改 package.json 設置不同的模式:
"scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" },
重新執行 npm run dev
或 npm run build
即可看到不同的打包結果:
我們可以看到兩種模式的結果完全不同,下面我們會更深入的按照我們真實的需求來講解一些常用配置。
接下來這個配置是最常用到的,我們使用 webpack 的主要目的之一就是為了更好的支撐前段模塊化的能力,既然需要模塊化當然少不了代碼分割,目前代碼分割有以下幾種:
- 通過
entry
分割不同入口,常用於多頁應用; - 通過
CommonsChunkPlugin
插件來分割不同功能模塊; - 通過動態
import
來分割。
下面我們主要講解 webpack 4.0.0-beta.0 版本的重大變化刪除了 CommonsChunkPlugin
插件。
3.3 刪除 CommonsChunkPlugin
webpack 4.0.0-beta.0刪除了
CommonsChunkPlugin
,以支持兩個新的選項(optimization.splitChunks
和optimization.runtimeChunk
)。
從webpack 4.0.0-beta.0 開始分割 Chunk
將不在使用 CommonsChunkPlugin
插件,而是使用 optimization
配置項,具體的實現原理可以參考CommonsChunkPlugin 。
由於還沒有正式官方文檔出來,以下是我們通過實踐出的 optimization
配置方法:
其中用到了新增的 splitChunks
屬性,此屬性看字面意思就明白是分割代碼塊的選項,其下可配置項已在下面示例代碼中列出(有興趣的朋友可以自行實踐):
entry: { vendor: ['lodash'] }, ... optimization: { splitChunks: { chunks: "initial", // 必須三選一: "initial" | "all"(默認就是all) | "async" minSize: 0, // 最小尺寸,默認0 minChunks: 1, // 最小 chunk ,默認1 maxAsyncRequests: 1, // 最大異步請求數, 默認1 maxInitialRequests : 1, // 最大初始化請求書,默認1 name: function(){}, // 名稱,此選項可接收 function cacheGroups:{ // 這里開始設置緩存的 chunks priority: 0, // 緩存組優先級 vendor: { // key 為entry中定義的 入口名稱 chunks: "initial", // 必須三選一: "initial" | "all" | "async"(默認就是異步) test: /react|lodash/, // 正則規則驗證,如果符合就提取 chunk name: "vendor", // 要緩存的 分隔出來的 chunk 名稱 minSize: 0, minChunks: 1, enforce: true, maxAsyncRequests: 1, // 最大異步請求數, 默認1 maxInitialRequests : 1, // 最大初始化請求書,默認1 reuseExistingChunk: true // 可設置是否重用該chunk(查看源碼沒有發現默認值) } } } },
以上就是 optimization.splitChunks
的所有可用的配置項屬性。