loader 基本上都是第三方類庫,使用時需要安裝,有一些 loader 還需要安裝額外的類庫,例如 less-loader 需要 less,babel-loader 需要 babel 等。
loader 匹配規則
loader的配置是放在 module 字段下,如下代碼前面提到過:
module.exports = { // ... module: { rules: [ { test: /\.jsx?/, include: [ path.resolve(__dirname, 'src'), // 指定哪些路徑下的文件需要經過 loader 處理 ], use: { loader: 'babel-loader', // 指定使用的 loader options: { presets: ['@babel/preset-env'], }, }, }, // 一個 object 即一條規則 // ... ], }, }
loader 的匹配規則中有兩個最關鍵的因素:一個是匹配條件,一個是匹配規則后的應用。
匹配條件通常都使用請求資源文件的絕對路徑來進行匹配,在官方文檔中稱為 resource,除此之外還有比較少用到的 issuer,則是聲明依賴請求的源文件的絕對路徑。
如下例子:
printer.js // Resource: greeter.js, Issuer: printer.js import greetSource from "./greeter"; index.js // Resource: greeter.js, Issuer: index.js import greet from "./greeter";
issuer:和resource有異曲同工的作用,不過區別在於它是將這個rule應用於哪個文件以及這個文件所導入的所有依賴文件
舉個例子:在 /path/to/app.js 中聲明引入 import './src/style.scss',resource 是「/path/to/src/style.scss」,issuer 是「/path/to/app.js」,規則條件會對這兩個值來嘗試匹配。
上述代碼中的 test 和 include 都用於匹配 resource 路徑,是 resource.test 和 resource.include 的簡寫,你也可以這么配置:
module.exports = { // ... rules: [ { resource: { // resource 的匹配條件 test: /\.jsx?/, include: [ path.resolve(__dirname, 'src'), ], }, // 如果要使用 issuer 匹配,便是 issuer: { test: ... } // ... }, // ... ], }
issuer 規則匹配的場景比較少見,你可以用它來嘗試約束某些類型的文件中只能引用某些類型的文件。
當規則的條件匹配時,便會使用對應的 loader 配置,如上述例子中的 babel-loader
規則條件配置
webpack 的規則提供了多種配置形式:
- { test: ... } 匹配特定條件
- { include: ... } 匹配特定路徑
- { exclude: ... } 排除特定路徑
- { and: [...] }必須匹配數組中所有條件
- { or: [...] } 匹配數組中任意一個條件
- { not: [...] } 排除匹配數組中所有條件
上述的所謂條件的值可以是:
- 字符串:必須以提供的字符串開始,所以是字符串的話,這里我們需要提供絕對路徑
- 正則表達式:調用正則的 test 方法來判斷匹配
- 函數:(path) => boolean,返回 true 表示匹配
- 數組:至少包含一個條件的數組
- 對象:匹配所有屬性值的條件
例子:
rules: [ { test: /\.jsx?/, // 正則 include: [ path.resolve(__dirname, 'src'), // 字符串,注意是絕對路徑 ], // 數組 // ... }, { resource: { test: { js: /\.js/, jsx: /\.jsx/, }, // 對象,不建議使用 not: [ (value) => { /* ... */ return true; }, // 函數,通常需要高度自定義時才會使用 ], } }, ],
test/include/exclude 是resource.(test/include/exclude)
的簡寫,and/or/not
這些則需要放到resource
中進行配置。
module type
webpack 4.x 版本強化了 module type,即模塊類型的概念,不同的模塊類型類似於配置了不同的 loader,webpack 會有針對性地進行處理,現階段實現了以下 5 種模塊類型。
javascript/auto
:即 webpack 3 默認的類型,支持現有的各種 JS 代碼模塊類型 —— CommonJS、AMD、ESMjavascript/esm
:ECMAScript modules,其他模塊系統,例如 CommonJS 或者 AMD 等不支持,是 .mjs 文件的默認類型javascript/dynamic
:CommonJS 和 AMD,排除 ESMjavascript/json
:JSON 格式數據,require 或者 import 都可以引入,是 .json 文件的默認類型webassembly/experimental
:WebAssembly modules,當前還處於試驗階段,是 .wasm 文件的默認類型
如果不希望使用默認的類型的話,在確定好匹配規則條件時,我們可以使用 type 字段來指定模塊類型,例如把所有的 JS 代碼文件都設置為強制使用 ESM 類型:
{ test: /\.js/, include: [ path.resolve(__dirname, 'src'), ], type: 'javascript/esm', // 這里指定模塊類型 },
上述做法是可以幫助你規范整個項目的模塊系統,但是如果遺留太多不同類型的模塊代碼時,建議還是直接使用默認的javascript/auto
。
使用 loader 配置
如下使用use字段:
rules: [ { test: /\.less/, use: [ 'style-loader', // 直接使用字符串表示 loader { loader: 'css-loader', options: { importLoaders: 1 }, }, // 用對象表示 loader,可以傳遞 loader 配置等 { loader: 'less-loader', options: { noIeCompat: true }, // 傳遞 loader 配置 }, ], }, ],
從上面的配置可以看到,use 字段可以是一個數組,也可以是一個字符串或者表示 loader 的對象。如果只需要一個 loader,也可以這樣:use: { loader: 'babel-loader', options: { ... } }
。
loader 應用順序
如下配置:
rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader", }, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", }, ],
這樣無法法保證 eslint-loader 在 babel-loader 應用前執行。webpack 在 rules
中提供了一個 enforce
的字段來配置當前 rule 的 loader 類型,沒配置的話是普通類型,我們可以配置 pre
或 post
,分別對應前置類型或后置類型的 loader。
eslint-loader 要檢查的是人工編寫的代碼,如果在 babel-loader 之后使用,那么檢查的是 Babel 轉換后的代碼,所以必須在 babel-loader 處理之前使用。
還有一種行內 loader,即我們在應用代碼中引用依賴時直接聲明使用的 loader,如 const json = require('json-loader!./file.json')
這種。不建議在應用開發中使用這種 loader
所有的 loader 按照前置 -> 行內 -> 普通 -> 后置的順序執行。所以當我們要確保 eslint-loader 在 babel-loader 之前執行時,可以如下添加 enforce 配置:
rules: [ { enforce: 'pre', // 指定為前置類型 test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader", }, ]
使用 noParse
module.noParse
字段:可以用於配置哪些模塊文件的內容不需要進行解析。對於一些不需要解析依賴(即無依賴)的第三方大型類庫等,可以通過這個字段來配置,以提高整體的構建速度。
注意:使用 noParse 進行忽略的模塊文件中不能使用import、require、define 等導入機制。
如下代碼:
module.exports = { // ... module: { noParse: /jquery|lodash/, // 正則表達式 // 或者使用 function noParse(content) { return /jquery|lodash/.test(content) }, } }