webpack的工作机制是基于事件流,将各个插件串联起来,而核心就是tapable对象

核心概念:
  • tapable 对象,是webpackplugin创建hook的包,中文文档
  • 开发webpack插件最重要两个资源是对象:compilercompilation,这两货都是扩展了tapable对象

    • Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
    • ompilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。

webpack 插件由以下组成:
  • 一个 JavaScript 命名函数。
  • 在插件函数的 prototype 上定义一个 apply 方法。
  • 指定一个绑定到 webpack 自身的事件钩子
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调。

下面是一个简单的插件结构:

// 定义一个class
class MyExampleWebpackPlugin {
  // 定义 apply方法,参数是 compiler
  apply(compiler) {
    // 附加一个指定的钩子
    compiler.hooks.emit.tap(
      'MyExampleWebpackPlugin',
      (compilation) => {
        // Manipulate the build using the plugin API provided by webpack
        compilation.addModule(/* ... */);
        compilation.hooks.buildModule.tap('MyExampleWebpackPlugin', () => {
        //do something...
      });
      }
    );
  }
}

在同步钩子中, tap是唯一的绑定方法,异步钩子通常支持异步插件

// promise: 绑定promise钩子的API
myCar.hooks.calculateRoutes.tapPromise("GoogleMapsPlugin", (source, target, routesList) => {
    // return a promise
    return google.maps.findRoute(source, target).then(route => {
        routesList.add(route);
    });
});
// tapAsync:绑定异步钩子的API
compilation.hooks.additionalAssets.tapAsync('MyPlugin', callback => {
  download('https://img.shields.io/npm/v/webpack.svg', function(resp) {
    if(resp.status === 200) {
      compilation.assets['webpack-version.svg'] = toAsset(resp);
      callback();
    } else {
      callback(new Error('[webpack-example-plugin] Unable to download the image'));
    }
  });
});
tapable 的钩子类型:

每一个钩子都可以tap 一个或者多个函数, 他们如何运行,取决于他们的钩子类型

  • 基本的钩子, (钩子类名没有waterfall, Bail, 或者 Loop 的 ), 这个钩子只会简单的调用每个tap进去的函数
  • Waterfall, 一个waterfall 钩子,也会调用每个tap进去的函数,不同的是,他会从每一个函数传一个返回的值到
    下一个函数
  • Bail, Bail 钩子允许更早的退出,当任何一个tap进去的函数,返回任何值, bail类会停止执行其他的函数执行.(类似 Promise.race())
  • Loop, TODO(我.... 这里也没描述,应该是写文档得时候 还没想好这个要怎么写,我尝试看他代码去补全,不
    过可能需要点时间.)

此外,钩子可以是同步的,也可以是异步的,Sync, AsyncSeries 和 AsyncParallel ,从名字就可以看出,哪些是可以绑定异步函数的

  • Sync, 一个同步钩子只能tap同步函数, 不然会报错.
  • AsyncSeries, 一个 async-series 钩子 可以tap 同步钩子, 基于回调的钩子(我估计是类似chunk的东西)和一个基于promise的钩子(使用myHook.tap(), myHook.tapAsync() 和 myHook.tapPromise().).他会按顺序的调用每个方法.
  • AsyncParallel, 一个 async-parallel 钩子跟上面的 async-series 一样 不同的是他会把异步钩子并行执行(并行执行就是把异步钩子全部一起开启,不按顺序执行).
实战,编写一个webpack插件

class FileListPlugin{
  apply (compiler) {
    compiler.hooks.emit.tap('FileListPlugin', function(compilation) {
      // 在生成文件中,创建一个头部字符串:
      var filelist = 'In this build:\n\n';
  
      // 遍历所有编译过的资源文件,
      // 对于每个文件名称,都添加一行内容。
      for (var filename in compilation.assets) {
        filelist += ('- '+ filename +'\n');
      }
  
      // 将这个列表作为一个新的文件资源,插入到 webpack 构建中:
      compilation.assets['filelist.md'] = {
        // 返回文件内容
        source: function() {
          //  既可以是代表文本文件的字符串,也可以是代表二进制文件的 Buffer
          return filelist;
        },
        // 返回文件大小
        size: function() {
          return filelist.length;
        }
      };
    });
  }
}

renbuzhudek
1 声望0 粉丝

落魄前端,在线炒粉,5元一份!