webpack編寫一個plugin插件

插件向第三方開發者提供了 webpack 引擎中完整的能力。使用階段式的構建回調,開發者能夠引入它們本身的行爲到 webpack 構建流程中。建立插件比建立 loader 更加高級,由於你將須要理解一些 webpack 底層的內部特性來實現相應的鉤子。webpack

1、插件由如下部分構成

  一、一個具名 JavaScript 函數web

  二、在它的原型上定義 apply 方法。api

  三、指定一個觸及到 webpack 自己的 事件鉤子數組

  四、操做 webpack 內部的實例特定數據。promise

  五、在實現功能後調用 webpack 提供的 callback。  架構

// 一個 JavaScript class
class MyExampleWebpackPlugin {
  // 將 `apply` 定義爲其原型方法,此方法以 compiler 做爲參數
  apply(compiler) {
    // 指定要附加到的事件鉤子函數
    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);

        // 使用 webpack 提供的 plugin API 操做構建結果
        compilation.addModule(/* ... */);

        callback();
      }
    );
  }
}

2、基本插件架構

插件是由一個構造函數(此構造函數上的 prototype 對象具備 apply 方法)的所實例化出來的。這個 apply方法在安裝插件時,會被 webpack compiler 調用一次。apply 方法能夠接收一個 webpack compiler 對象的引用,從而能夠在回調函數中訪問到 compiler 對象。app

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* 在 hook 被觸及時,會將 stats 做爲參數傳入。 */
    ) => {
      console.log('Hello World!');
    });
  }
}

module.exports = HelloWorldPlugin;

而後,要使用這個插件,在你的 webpack 配置的 plugins 數組中添加一個實例:異步

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

module.exports = {
  // ... 這裏是其餘配置 ...
  plugins: [new HelloWorldPlugin({ options: true })]
};

3、compiler 和 compilation

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

class HelloCompilationPlugin {
  apply(compiler) {
    // tap(觸及) 到 compilation hook,而在 callback 回調時,會將 compilation 對象做爲參數,
    compiler.hooks.compilation.tap('HelloCompilationPlugin', compilation => {
      // 如今,經過 compilation 對象,咱們能夠 tap(觸及) 到各類可用的 hooks 了
      compilation.hooks.optimize.tap('HelloCompilationPlugin', () => {
        console.log('正在優化資源。');
      });
    });
  }
}

module.exports = HelloCompilationPlugin;

這裏列出 compilercompilation 和其餘重要對象上可用 hooks,請查看 插件 API 文檔。函數

4、異步事件鉤子

有些插件 hooks 是異步的。想要 tap(觸及) 某些 hooks,咱們可使用同步方式運行的 tap 方法,或者使用異步方式運行的 tapAsync 方法或 tapPromise 方法。

tapAsync

在咱們使用 tapAsync 方法 tap 插件時,咱們須要調用 callback,此 callback 將做爲最後一個參數傳入函數。

class HelloAsyncPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync('HelloAsyncPlugin', (compilation, callback) => {
      // 作一些異步的事情……
      setTimeout(function() {
        console.log('Done with async work...');
        callback();
      }, 1000);
    });
  }
}

module.exports = HelloAsyncPlugin;

tapPromise

在咱們使用 tapPromise 方法 tap 插件時,咱們須要返回一個 promise,此 promise 將在咱們的異步任務完成時 resolve。

class HelloAsyncPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapPromise('HelloAsyncPlugin', compilation => {
      // 返回一個 Promise,在咱們的異步任務完成時 resolve……
      return new Promise((resolve, reject) => {
        setTimeout(function() {
          console.log('異步工做完成……');
          resolve();
        }, 1000);
      });
    });
  }
}

module.exports = HelloAsyncPlugin;

5、示例

一旦能咱們深刻理解 webpack compiler 和每一個獨立的 compilation,咱們就能經過 webpack 引擎自己作到無窮無盡的事情。咱們能夠從新格式化已有的文件,建立衍生的文件,或者製做全新的生成文件。

咱們來寫一個簡單的示例插件,生成一個叫作 filelist.md 的新文件;文件內容是全部構建生成的文件的列表。這個插件大概像下面這樣:

class FileListPlugin {
  apply(compiler) {
    // emit 是異步 hook,使用 tapAsync 觸及它,還可使用 tapPromise/tap(同步)
    compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback) => {
      // 在生成文件中,建立一個頭部字符串:
      var filelist = 'In this build:\n\n';

      // 遍歷全部編譯過的資源文件,
      // 對於每一個文件名稱,都添加一行內容。
      for (var filename in compilation.assets) {
        filelist += '- ' + filename + '\n';
      }

      // 將這個列表做爲一個新的文件資源,插入到 webpack 構建中:
      compilation.assets['filelist.md'] = {
        source: function() {
          return filelist;
        },
        size: function() {
          return filelist.length;
        }
      };

      callback();
    });
  }
}

module.exports = FileListPlugin;

 6、參考博客:

  http://www.javashuo.com/article/p-ttmejbzp-he.html

相關文章
相關標籤/搜索