如何设计一个webpack插件?

From Flickr
From Flickr

安装webpack

首先我们创建一个文件夹webpack-plugin,执行:

1
2
npm init
npm install webpack --save-dev

如果你使用的是webpack 4+版本那么你还应该安装webpack-cli

1
npm install --save-dev webpack-cli

安装webpcak-dev-server

这个插件类似一个web服务器,不过它多了许多配置,我们通过配置它可以为我们时时编译刷新网页,为我们的开发带来方便,提高开发效率

1
npm install --save-dev webpack-dev-server

创建webpack.config.js

该文件是webpack 在打包以及启动服务时候所依赖的配置文件,我们也可以更改它的名称,这取决于以运行命令时候所依赖的package.json 文件中”script’字段配置,如图:

package.json
package.json

结构目录

结构目录
结构目录

编写一个插件

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子,所以做好阅读一些源码的准备!

创建插件

webpack 插件由以下组成:

  • 一个构造函数
  • 在插件函数的prototype(原型属性)上添加一个apply方法
  • 将插件函数实例挂载在webpack自身勾子函数
  • 处理 webpack 内部实例的特定数据
  • 功能完成后调用 webpack 提供的回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个插件类
class Hello{
constructor(option){
this.option = option
}
// 在插件函数的 prototype 上定义一个 `apply` 方法。
apply(compiler){
// 指定一个挂载到 webpack 自身的事件钩子。
compiler.plugin('done',(compilation)=>{
console.log("我是hello插件");

// 功能完成后调用 webpack 提供的回调。
callback();
})
}
}

module.exports = Hello;

Compiler 和 Compilation

在插件开发中最重要的两个参数就是 compiler 和 compilation 对象。理解它们的角色是扩展 webpack 引擎重要的第一步。

  • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。

  • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

详解Compiler

编译器钩子

Compiler模块是主引擎,它使用通过CLI或Node API传递的所有选项创建编译实例。它扩展了 Tapable类以注册和调用插件。大多数面向用户的插件首先在Compiler内。该模块是公开的webpack.Compiler,可以直接使用。

监听

Compiler监视文件系统和重新编译的文件改变。在观察模式中compiler会调用一些额外的事件如:watchRun,watchClose和invalid。这些事件通常应用于开发,webpack-dev-server就是一个典型的应用,我们在编写代码时候不用从新编译,webpack-dev-server会帮助我们时时编译。

勾子函数

  1. entryOption
  • 同步勾子函数
  • 在处理了webpack选项的entry配置后调用。
  • 回调参数:context,entry
    1
    2
    3
    compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
    /* ... */
    });
  1. afterPlugins
  • 同步函数
  • 在设置初始内部插件集后调用。
  • 回调参数: compiler

简单介绍两个如果大家有兴趣可以访问这里去学习。

详解Compiler

Compiler介绍

该Compilation模块用于Compiler创建新的编译(或构建)。compilation实例有权访问所有的模块和它们的依赖)。它是应用程序依赖关系图中所有模块的文字汇编。在编译阶段,模块被加载,密封,优化,分块,散列和恢复。

调用勾子函数

Compilation和compiler 一样具有针对不同生命周期的很多勾子函数,使用方法也差不多

1
compilation.hooks.someHook.tap(/* ... */);

在这里针对compilation的勾子函数我们就不一一介绍啦,如果您有兴趣可以点击这里进行学习

安装插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// webpack.config.js

const path = require('path');
const Hello = require('./plugin/hello.js');

module.exports = {
//...
plugins:[
new Hello()
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
inline:false,
hot: true,
}
};

命令行执行:

1
webpack-dev-server

你可以看到命令行在执行完编译后将会打印:

1
我是hello插件

总结

对于编写一个webpack插件,我们主要了解Compiler 和 Compilation他们的各种勾子函数,我们需要在什么时候做什么事,就清晰多了。谢谢。