[譯]如何寫一個webpack插件

原文:how to write a pluginjavascript

譯者:neal1991java

welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact mewebpack

LICENSE: MITgit

插件可以將webpack引擎的所有潛力暴露給第三方的開發者。經過使用階段構建回調,開發者可以將他們本身的行爲引入到webpack的構建過程當中。構建插件比構建loader更高級,由於你須要理解一些webpack低層次的內部鉤子。準備好閱讀一些源代碼吧!github

Compiler以及Compilation

在開發插件的時候最重要的兩個資源就是compilercompilation對象。理解它們的角色是拓展webpack引擎重要的第一步。web

  • compiler對象表明了完整的配置的webpack環境。一旦開啓webpack以後,這個對象就被構建了,而且這個對象會使用全部操做設置,包括options, loaders, 以及plugins來進行配置。當將一個插件應用到webpack環境中,這個插件將會得到一個對於這個compiler的引用。使用這個compiler能夠訪問主要的webpack環境。數組

  • 一個compilation對象表明版本資源的一次構建。當運行webpack開發中間件的時候,每次檢測到文件變化的時候都會產生一個新的compilation,所以會生成一系列編譯後的資源。Compilation表示有關模塊資源,已編譯資源,已更改文件和監視依賴關係的當前狀態的信息。該compilation還提供了許多回調點,插件能夠選擇執行自定義操做。架構

這兩個組件是任何webpack插件(特別是compilation)的內部一部分,所以開發者熟悉這些源代碼文件以後將會受益非凡:app

基本的插件架構

插件是實例對象,而且在它們的prototype上,會有一個apply方法。當安裝這個插件的時候,這個apply方法就會被webpack compiler調用。這個apply會給出一個對於潛在的webpack compiler的引用,保證了對於compiler回調的訪問。一個簡單的插件結構以下:

function HelloWorldPlugin(options) {
  // Setup the plugin instance with options...
}

HelloWorldPlugin.prototype.apply = function(compiler) {
  compiler.plugin('done', function() {
    console.log('Hello World!'); 
  });
};

module.exports = HelloWorldPlugin;

接着是安裝這個插件,只要在你的webpack 配置plugins數組裏面添加一個實例:

var HelloWorldPlugin = require('hello-world');

var webpackConfig = {
  // ... config settings here ...
  plugins: [
    new HelloWorldPlugin({options: true})
  ]
};

訪問compilation

使用compiler對象,你可能綁定提供那個對於每個新的compilation引用的回調。這些compilation提供對於在構建過程當中對於不少步驟鉤子的回調。

function HelloCompilationPlugin(options) {}

HelloCompilationPlugin.prototype.apply = function(compiler) {

  // Setup callback for accessing a compilation:
  compiler.plugin("compilation", function(compilation) {
    
    // Now setup callbacks for accessing compilation steps:
    compilation.plugin("optimize", function() {
      console.log("Assets are being optimized.");
    });
  });
};

module.exports = HelloCompilationPlugin;

對於更多關於compiler以及compilation上的回調以及其餘重要的對象,請參考 [[plugins API|plugins]] 文檔。

異步compilation plugins

有一些compilation插件步驟是異步的,而且當你的插件完成運行的時候,傳遞一個必須被調用的回調函數。

function HelloAsyncPlugin(options) {}

HelloAsyncPlugin.prototype.apply = function(compiler) {
  compiler.plugin("emit", function(compilation, callback) {

    // Do something async...
    setTimeout(function() {
      console.log("Done with async work...");
      callback();
    }, 1000);

  });
};

module.exports = HelloAsyncPlugin;

一個簡單的例子

一旦咱們能夠鎖定到webpack compiler以及每個獨立的compilation,咱們能夠利用引擎自己就能發揮無窮的潛力。咱們可以從新格式化存在的文件,建立衍生文件,或者製造全新的資源。

讓咱們寫一個簡單的可以生成一個新的打包文件filelist.md的插件例子;這個文件的內容會列出全部存在咱們build以內的資源文件。這個插件可能看起來是這個樣子的:

function FileListPlugin(options) {}

FileListPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    // Create a header string for the generated file:
    var filelist = 'In this build:\n\n';

    // Loop through all compiled assets,
    // adding a new line item for each filename.
    for (var filename in compilation.assets) {
      filelist += ('- '+ filename +'\n');
    }
    
    // Insert this list into the Webpack build as a new file asset:
    compilation.assets['filelist.md'] = {
      source: function() {
        return filelist;
      },
      size: function() {
        return filelist.length;
      }
    };

    callback();
  });
};

module.exports = FileListPlugin;

有用的插件模式

插件容許在webpack構建系統內發揮無盡量的定製化。這容許你建立自定義的資源類型,執行特殊的構建調整,或者設置在使用中間件的時候進一步提高webpack運行時間。下面的webpack的一些特性在開發插件的時候變得頗有用。

探索assets, chunks, modules, 以及dependencies

在compilation完成以後,compilation中的全部的結構均可能被遍歷。

function MyPlugin() {}

MyPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    
    // Explore each chunk (build output):
    compilation.chunks.forEach(function(chunk) {
      // Explore each module within the chunk (built inputs):
      chunk.modules.forEach(function(module) {
        // Explore each source file path that was included into the module:
        module.fileDependencies.forEach(function(filepath) {
          // we've learned a lot about the source structure now...
        });
      });

      // Explore each asset filename generated by the chunk:
      chunk.files.forEach(function(filename) {
        // Get the asset source for each file generated by the chunk:
        var source = compilation.assets[filename].source();
      });
    });

    callback();
  });
};

module.exports = MyPlugin;
  • compilation.modules: 在compilation中由模塊(構建輸入)組成的數組。每一個模塊管理來自於源代碼庫中的源文件的構建。

  • module.fileDependencies: 包含在模塊中的源文件路徑數組。 這包括源JavaScript文件自己(例如:index.js)以及所需的全部依賴項資源文件(樣式表,圖像等)。 查看依賴關係對於查看哪些源文件屬於模塊頗有用。

  • compilation.chunks: Compilation中由chunks組成的數組(構建輸出)。 每一個chunk管理最終渲染資源的組合。

  • chunk.modules: 包含在一個chunk中的模塊數組。 經過擴展,您能夠查看每一個模塊的依賴關係,以查看傳遞到chunk中的原始源文件

  • chunk.files: 由chunk生成的輸出文件名的數組。 您能夠從compilation.assets表訪問這些資源。

檢測觀察圖

在運行webpack中間件時,每一個compilation都包含一個fileDependencies數組(正在監視的文件)和一個將觀察文件路徑映射到時間戳的fileTimestamps哈希。 這些對於檢測compilation中哪些文件已更改很是有用:

function MyPlugin() {
  this.startTime = Date.now();
  this.prevTimestamps = {};
}

MyPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    
    var changedFiles = Object.keys(compilation.fileTimestamps).filter(function(watchfile) {
      return (this.prevTimestamps[watchfile] || this.startTime) < (compilation.fileTimestamps[watchfile] || Infinity);
    }.bind(this));
    
    this.prevTimestamps = compilation.fileTimestamps;
    callback();
  }.bind(this));
};

module.exports = MyPlugin;

您還能夠將新的文件路徑傳入觀察圖,以便在這些文件更改時接收compilation觸發器。 只需將有效的文件路徑推送到compilation.fileDependencies數組中便可將其添加到觀察列表中。 注意:在每一個compilation中重建fileDependencies數組,所以您的插件必須將本身觀察的依賴項推送到每一個編譯中,以使它們保持監視。

改變的chunks

與觀察圖相似,經過跟蹤它們的哈希值,能夠在compilation中監視更改的塊(或模塊)。

function MyPlugin() {
  this.chunkVersions = {};
}

MyPlugin.prototype.apply = function(compiler) {
  compiler.plugin('emit', function(compilation, callback) {
    
    var changedChunks = compilation.chunks.filter(function(chunk) {
      var oldVersion = this.chunkVersions[chunk.name];
      this.chunkVersions[chunk.name] = chunk.hash;
      return chunk.hash !== oldVersion;
    }.bind(this));
    
    callback();
  }.bind(this));
};

module.exports = MyPlugin;
相關文章
相關標籤/搜索