如何编写一个-plugin

36次阅读

共计 3096 个字符,预计需要花费 8 分钟才能阅读完成。

创建一个 plugin

webpack 的插件组成有以下这几部分:

  1. 一个 javascript function 或者一个 class
  2. prototype 上定义 apply 方法。
  3. 指定要插入的事件挂钩。
  4. 使用 webpack 提供的插件 API 操作构建。
  5. 在功能完成后调用 webpack 提供的回调函数。
// 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();}
    );
  }
}

基本的插件架构

plugin 是在其prototype 上使用 apply 方法实例化的对象。这个 apply 方法在安装插件时由 webpack 编译器调用一次。apply 方法被赋予对底层 webpack 编译器的引用,该引用授予对编译器回调的访问权。一个简单的插件结构如下:

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

module.exports = HelloWorldPlugin;

Compiler and Compilation

开发插件时最重要的两种资源是 Compiler 和 Compilation。

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;

Async event hooks 异步事件钩子

有些插件挂钩是异步的。为了利用它们,我们可以使用 tap 方法,它将以同步的方式运行,或者使用 tapAsync 方法或 tapprogre 方法之一,这是异步方法。

tapAsync

当我们使用 tapAsync 方法访问插件时,我们需要调用回调函数,它是函数的最后一个参数。

class HelloAsyncPlugin {apply(compiler) {compiler.hooks.emit.tapAsync('HelloAsyncPlugin', (compilation, callback) => {
      // Do something async...
      setTimeout(function() {console.log('Done with async work...');
        callback();}, 1000);
    });
  }
}

module.exports = HelloAsyncPlugin;

tapPromise

当我们使用 tapPromise 方法定义插件时,我们需要返回一个 promise,这个 promise 将在异步任务完成时 resolve。

class HelloAsyncPlugin {apply(compiler) {
    compiler.hooks.emit.tapPromise('HelloAsyncPlugin', compilation => {
      // return a Promise that resolves when we are done...
      return new Promise((resolve, reject) => {setTimeout(function() {console.log('Done with async work...');
          resolve();}, 1000);
      });
    });
  }
}

module.exports = HelloAsyncPlugin;

Example

一旦我们能够使用 webpack 编译器和每个单独的编译器,我们就可以对引擎本身进行无穷无尽的操作。我们可以重新格式化现有文件,创建派生文件,或者创建全新的资源。

让我们编写一个简单的示例插件,它生成一个名为 fillist .md 的新构建文件; 其中的内容将列出构建中的所有资源文件。这个插件可能看起来像这样:

class FileListPlugin {apply(compiler) {// emit is asynchronous hook, tapping into it using tapAsync, you can use tapPromise/tap(synchronous) as well
    compiler.hooks.emit.tapAsync('FileListPlugin', (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;

Different Plugin Shapes

插件可以根据它所访问的事件挂钩被划分为不同的类型。每个事件钩子都预先定义为同步 (synchronous) 或异步(asynchronous)、瀑布式或并行钩子,并且钩子在内部使用 call/callAsync 方法调用。受支持的或可以使用的钩子列表通常指定在 this.hooks 上。

正文完
 0