Plugins expose the full potential of the webpack engine to third-party developers. ----------- Webpack
相比於 loaders,plugin 更加的靈活,因為它能夠接觸到 webpack 編譯器和編譯核心。這就使得 plugin 可以通過一些 hook 函式來攔截 webpack 的執行,甚至你可以執行一個子編譯器和 loader 串聯,像 MiniCssExtractPlugin
就是這麼做的。
示例程式碼:link
webpack plugin 基本結構
以 html-webpack-plugin
為例,它的使用如下
plugins: [
new HtmlWebpackPlugin({
...
}),
],
複製程式碼
不難看出,webpack plugin 的基本形式一個建構函式 new function()
,同時為了能夠獲得 compiler,就需要 plugin 對外暴露一個介面(為 apply
函式)。所以,它的基本結構如下:
- 一個命名的 JavaScript 函式物件;
- 在其
prototype
上,定義一個apply
方法。
JavaScript 的實現這種形式的方法有很多,本文采用 class
來實現,具體如下
module.exports = class DemoPlugin {
apply() {
console.log('applying')
}
}
複製程式碼
配置開發環境
為了能夠執行這個 plugin,我們需要建立一個環境
mkdir webpack-demo-plugin
cd webpack-demo-plugin
npm init
複製程式碼
webpack.plugin.js
const path = require("path");
const PATHS = {
lib: path.join(__dirname, "index.js"),
build: path.join(__dirname, "build"),
};
module.exports = {
entry: {
lib: PATHS.lib,
},
output: {
path: PATHS.build,
filename: "[name].js",
},
};
複製程式碼
index.js
console.log("hello world")
複製程式碼
同時向 package.json 中新增
"scripts": {
"build:plugin": "webpack --config webpack.plugin.js --mode production",
...
}
複製程式碼
實現 webpack demo
建立 plugins/demo-plugin.js 檔案,內容為之前的 webpack plugin demo,並將其引入到 webpack.plugin.js 內。
webpack.plugin.js
const DemoPlugin = require("./plugins/demo-plugin.js");
module.exports = {
...
// 引入 plugin
plugins: [new DemoPlugin()],
};
複製程式碼
嘗試執行下 npm run build:plugin
,終端上列印出
applying
Hash: 98c8997160aa995a58a4
Version: webpack 4.12.0
Time: 93ms
Built at: 2019-04-29 14:34:31
Asset Size Chunks Chunk Names
lib.js 956 bytes 0 [emitted] lib
[0] ./index.js 26 bytes {0} [built]
複製程式碼
驚奇的發現 applying,說明外掛已經成功執行。
傳遞引數
在應用一個 plugin 時,有時需要根據傳遞 Options 來告訴 plugin 具體應該做什麼。當 new DemoPlugin()
時候會觸發 class DemoPlugin
的 constructor
所以
plugins/demo-plugin.js
module.exports = class DemoPlugin {
constructor(options) {
this.options = options
}
apply() {
console.log('apply', this.options)
}
}
複製程式碼
同時,還需要修改 webpack.plugin.js 來傳遞對應引數
module.exports = {
...
plugins: [new DemoPlugin({ name: 'demo' })],
}
複製程式碼
執行 npm run build:plugin
,可以發現 apply { name: 'demo' }
。這裡介紹一個常用外掛 schema-utils 能夠用來校驗 Options。
理解 webpack 的 compiler 和 compilation
在之前的 webpack plugin 基本結構中介紹,apply
函式能夠用來訪問 webpack 的核心。具體的做法是,apply
函式的引數為 compiler
plugins/demo-plugin.js
module.exports = class DemoPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
console.log(compiler)
}
}
複製程式碼
再次執行 npm run build:plugin
,會發現終端上列印出了 compiler
的全部資訊,其中 hooks
欄位佔了絕大部分。
對照著官方文件,你會發現每一個 hook 對應一個特定的階段。 例如,emit 實踐是在向輸出目錄傳送資源之前執行。這樣就可以通過監聽 hook 來實現控制編譯過程。
plugins/demo-plugin.js
module.exports = class DemoPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
compiler.plugin('emit', (compilation, next) => {
console.log(compilation)
next()
})
}
}
複製程式碼
不要忘記呼叫 next,否則 webpack 將不會繼續打包。
執行 npm run build:plugin
會顯示出比以前更多的資訊,因為編譯物件包含webpack 遍歷的整個依賴關係圖。 你可以訪問與此相關的所有內容,包括 entries, chunks, modules, assets等。
通過 Compilation 寫入檔案
可以通過 compilation
的 assets
物件來編寫新的檔案,或是修改已經建立的檔案。為了更好地寫入檔案,我們引入一個 npm 包
npm install webpack-sources --save-dev
複製程式碼
plugins/demo-plugin.js
const { RawSource } = require("webpack-sources");
module.exports = class DemoPlugin {
constructor(options) {
this.options = options
}
apply(compiler) {
const { name } = this.options;
compiler.plugin('emit', (compilation, next) => {
compilation.assets[name] = new RawSource("demo");
next()
})
}
}
複製程式碼
在終端執行 npm run build:plugin
Hash: 98c8997160aa995a58a4
Version: webpack 4.12.0
Time: 95ms
Built at: 2019-04-29 16:08:52
Asset Size Chunks Chunk Names
lib.js 956 bytes 0 [emitted] lib
demo 4 bytes [emitted]
[0] ./index.js 26 bytes {0} [built]
複製程式碼
在 Asset 那裡一列內,出現了我們自定的 demo 檔案。
更多的鉤子函式,請見 the official compilation reference。
管理 Warnings 和 Errors
做一個實驗,如果你在 apply
函式內插入 throw new Error("Message")
,會發生什麼,終端會列印出 Unhandled rejection Error: Message
。然後 webpack 中斷執行。
為了不影響 webpack 的執行,要在編譯期間向使用者發出警告或錯誤訊息,則應使用 compilation.warnings
和 compilation.errors
。
compilation.warnings.push("warning");
compilation.errors.push("error");
複製程式碼
總結
當你著手開始設計外掛時,一定要花時間研究同型別的現有外掛。 逐個開發外掛,以便你一次只驗證一個外掛。 遇到問題,可以檢視 webpack 原始碼,它會增強你的 debug 直覺。
參考: