如何写一个 webpack plugin?

296 阅读1分钟

这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。

如何写一个 webpack plugin

什么是 plugin

在webpack运行的声明周期中会广播许多事件,plugin可以监听这些事件,在特定的时刻调用webpack提供的API执行相应的操作。

plugin 和 loader 的区别

上一章简单介绍了什么是 loader 以及如何编写一个简单的 loader,那么 plugin 和 loader 有什么区别?

loader我们通常翻译为“加载器”,它提供了在webpack打包后对源码字符串的转译能力,尤其是对非 javascript 的文件。

plugin我们通常翻译为“插件”,插件可以让webpack的变得更加灵活。Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

如何写一个 plugin

1.创建文件 index.js

image.png

console.log('hello ouda')

里面仅仅一行代码,打印 hello ouda

2.执行 webpack 打包

$yarn init -y
$yarn add webpack webpack-cli
$npx webpack

执行打包结果:

image.png

3.写一个 plugin

plugin 是一个具有 apply 方法的 JavaScript 对象。apply 方法会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。

plugin 的基本架构:

class myPlugin {
    constructor () {
 
    }
 
    apply (compiler) {
        
    }
}
 
module.exports = myPlugin

可以看到,这里的插件本质是一个 class , 他有一个 构造方法 conctuctor 和 一个 apply方法, apply方法有一个入参 compiler

3.1为什么要写 apply?

apply 方法在安装插件时,会被 webpack compiler 调用一次。apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象。

3.2什么是 compiler?

Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例

关于 compiler 的一些钩子: compiler api

3.3 获取 plugin 传递参数

在使用 plugin 时同时也是可以传递参数的,咱们 plugin 的本质是类,所以通过 new 的构造方法,可以在 plugin constructor 中被传递过来。

配置 webpack.config.js

const MyPlugin = require('./plugin/index')
module.exports = {
  entry: './src/index',
  plugins: [ // 使用自己写的 plugin
    new MyPlugin({
      name: 'ouda'
    })
  ]
}

在 插件中获取并且打印传递的参数:

plugin/index.js:

class MyPlugin {
  constructor(options) {
    this.options = options
    console.log('options: ' + JSON.stringify(this.options))
  }
  apply(compiler) {
    console.log('apply options: ' + JSON.stringify(this.options))
  }
}

module.exports = MyPlugin
$npx webpack

运行结果:

成功获得传递的参数。这些参数是可以在 apply 函数中被使用的。

image.png

3.4 写一个钩子

生命周期函数是由 compiler 暴露, 可以通过如下方式访问:

compiler.hooks.someHook.tap('MyPlugin', (params) => {
  /* ... */
});

关于 compiler 的一些钩子: compiler api

尝试调用一个 beforeRun 钩子:

class MyPlugin {
  constructor(options) {
    this.options = options
    console.log('options: ' + JSON.stringify(this.options))
  }
  apply(compiler) {
    compiler.hooks.beforeRun.tap('MyPlugin', (params) => {
      console.log('before run ', params)
    });
  }
}

module.exports = MyPlugin

打包后是能够看到打印结果的。

这样就完成了一个最简单的 plugin。