6-3 如何編寫一個 plugin

1. 簡介

插件向第三方開發(fā)者提供了 webpack 引擎中完整的能力。使用階段式的構(gòu)建回調(diào)洒嗤,開發(fā)者可以引入它們自己的行為到 webpack 構(gòu)建流程中。插件能夠 鉤入(hook) 到在每個編譯(compilation)中觸發(fā)的所有關(guān)鍵事件。在編譯的每一步,插件都具備完全訪問 compiler 對象的能力单寂,如果情況合適,還可以訪問當前 compilation 對象吐辙。

創(chuàng)建插件比創(chuàng)建 loader 更加高級宣决,因為你將需要理解一些 webpack 底層的內(nèi)部特性來做相應(yīng)的鉤子。

2. 創(chuàng)建插件

webpack 插件由以下組成:

  • 命名的 JavaScript 函數(shù)或 JavaScript 類
  • 在插件函數(shù)或類的 prototype 上定義一個 apply 方法昏苏。
  • 指定一個需要綁定的 event hook尊沸。
  • 處理 webpack 內(nèi)部實例的特定數(shù)據(jù)。
  • 功能完成后調(diào)用 webpack 提供的回調(diào)贤惯。
// A JavaScript class.
class MyExampleWebpackPlugin {
  // Define `apply` as its prototype method which is supplied with compiler as its argument
  apply(compiler) {
    // Specify the event hook to attach to
    compiler.hooks.emit.tapAsync(
      'MyExampleWebpackPlugin',
      (compilation, callback) => {
        console.log('This is an example plugin!');
        console.log('Here’s the `compilation` object which represents a single build of assets:', compilation);

        // Manipulate the build using the plugin API provided by webpack
        compilation.addModule(/* ... */);

        callback();
      }
    );
  }
}

3. 基礎(chǔ)的 plugin 結(jié)構(gòu)

插件是在其原型上擁有 apply 方法的實例化對象洼专。在安裝插件時,webpack編譯器會調(diào)用這個apply方法一次孵构。apply方法有一個對底層webpack編譯器的引用屁商,它授予對編譯器回調(diào)的訪問權(quán)。一個簡單的插件結(jié)構(gòu)如下:

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* stats is passed as an argument when done hook is tapped.  */
    ) => {
      console.log('Hello World!');
    });
  }
}

module.exports = HelloWorldPlugin;

這里回調(diào)函數(shù)的參數(shù) stats 其實結(jié)構(gòu)如下:


image.png

然后颈墅,要安裝這個插件棒假,只需要在你的 webpack 配置的 plugin 數(shù)組中添加一個實例:

// webpack.config.js
var HelloWorldPlugin = require('hello-world');

module.exports = {
  // ... configuration settings here ...
  plugins: [new HelloWorldPlugin({ options: true })]
};

還可以使用' schema-utils '來驗證通過插件 options 選項傳遞的參數(shù)。這里有一個例子:

import validateOptions from 'schema-utils';

// schema for options object
const schema = {
  type: 'object',
  properties: {
    test: {
      type: 'string'
    }
  }
};

export default class HelloWorldPlugin {

  constructor(options = {}){
    validateOptions(schema, options, {
        name: 'hello-world-plugin', // 報錯時精盅,輸出的插件名名稱。其他配置項參考 schema-utils 文檔
    });
  }

  apply(compiler) {}
}

4. Compiler 和 Compilation

在插件開發(fā)中最重要的兩個資源就是 compiler 和 compilation 對象谜酒。理解它們的角色是擴展 webpack 引擎重要的第一步叹俏。

  • compiler 對象代表了完整的 webpack 環(huán)境配置。這個對象在啟動 webpack 時被一次性建立僻族,并配置好所有可操作的設(shè)置粘驰,包括 options屡谐,loader 和 plugin。當在 webpack 環(huán)境中應(yīng)用一個插件時蝌数,插件將收到此 compiler 對象的引用愕掏。可以使用它來訪問 webpack 的主環(huán)境顶伞。

  • compilation 對象代表了一次資源版本構(gòu)建饵撑。當運行 webpack 開發(fā)環(huán)境中間件時,每當檢測到一個文件變化唆貌,就會創(chuàng)建一個新的 compilation滑潘,從而生成一組新的編譯資源。一個 compilation 對象表現(xiàn)了當前的模塊資源锨咙、編譯生成資源语卤、變化的文件、以及被跟蹤依賴的狀態(tài)信息酪刀。compilation 對象也提供了很多關(guān)鍵時機的回調(diào)粹舵,以供插件做自定義處理時選擇使用。

這兩個組件是任何 webpack 插件不可或缺的部分(特別是 compilation)骂倘,因此眼滤,開發(fā)者在閱讀源碼,并熟悉它們之后稠茂,會感到獲益匪淺:

class HelloCompilationPlugin {
  apply(compiler) {
    // Tap into compilation hook which gives compilation as argument to the callback function
    compiler.hooks.compilation.tap('HelloCompilationPlugin', compilation => {
      // Now we can tap into various hooks available through compilation
      compilation.hooks.optimize.tap('HelloCompilationPlugin', () => {
        console.log('Assets are being optimized.');
      });
    });
  }
}

module.exports = HelloCompilationPlugin;

有關(guān)compiler柠偶、compillation 和其他重要對象的鉤子列表,請參閱plugins API文檔睬关。

參考

writing-a-plugin
創(chuàng)建插件
api/plugins/
cn/api/plugins/
schema-utils

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诱担,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子电爹,更是在濱河造成了極大的恐慌蔫仙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丐箩,死亡現(xiàn)場離奇詭異摇邦,居然都是意外死亡,警方通過查閱死者的電腦和手機屎勘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門施籍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人概漱,你說我怎么就攤上這事丑慎。” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵竿裂,是天一觀的道長玉吁。 經(jīng)常有香客問我,道長腻异,這世上最難降的妖魔是什么进副? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮悔常,結(jié)果婚禮上影斑,老公的妹妹穿的比我還像新娘。我一直安慰自己这嚣,他們只是感情好鸥昏,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姐帚,像睡著了一般吏垮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罐旗,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天膳汪,我揣著相機與錄音,去河邊找鬼九秀。 笑死遗嗽,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的鼓蜒。 我是一名探鬼主播痹换,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼都弹!你這毒婦竟也來了娇豫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤畅厢,失蹤者是張志新(化名)和其女友劉穎冯痢,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體框杜,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡浦楣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了咪辱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片振劳。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖油狂,靈堂內(nèi)的尸體忽然破棺而出澎迎,到底是詐尸還是另有隱情庐杨,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布夹供,位于F島的核電站,受9級特大地震影響仁堪,放射性物質(zhì)發(fā)生泄漏哮洽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一弦聂、第九天 我趴在偏房一處隱蔽的房頂上張望鸟辅。 院中可真熱鬧,春花似錦莺葫、人聲如沸匪凉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽再层。三九已至,卻和暖如春堡纬,著一層夾襖步出監(jiān)牢的瞬間聂受,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工烤镐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛋济,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓炮叶,卻偏偏與公主長得像碗旅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子镜悉,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355