[译]如何写一个webpack插件

原文:how to write a plugin

译者:neal1991

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

LICENSE: MIT

插件能够将webpack引擎的悉数潜力暴露给第三方的开辟者。经由过程运用阶段构建回调,开辟者能够将他们自身的行动引入到webpack的构建过程当中。构建插件比构建loader更高等,由于你须要明白一些webpack低层次的内部钩子。准备好浏览一些源代码吧!

Compiler以及Compilation

在开辟插件的时刻最重要的两个资本就是compilercompilation对象。明白它们的角色是拓展webpack引擎重要的第一步。

  • compiler对象代表了完全的设置的webpack环境。一旦开启webpack以后,这个对象就被构建了,而且这个对象会运用一切操纵设置,包括options, loaders, 以及plugins来举行设置。当将一个插件应用到webpack环境中,这个插件将会取得一个关于这个compiler的援用。运用这个compiler能够接见重要的webpack环境。

  • 一个compilation对象代表版本资本的一次构建。当运转webpack开辟中间件的时刻,每次检测到文件变化的时刻都邑发生一个新的compilation,因而会天生一系列编译后的资本。Compilation示意有关模块资本,已编译资本,已变动文件和看管依靠关联的当前状况的信息。该compilation还供应了许多回调点,插件能够挑选实行自定义操纵。

这两个组件是任何webpack插件(特别是compilation)的内部一部分,因而开辟者熟习这些源代码文件以后将会受益特别:

基础的插件架构

插件是实例对象,而且在它们的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;
    原文作者:neal
    原文地址: https://segmentfault.com/a/1190000009411554
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞