webpack之创建一个plugin

190 阅读3分钟

1. plugin

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

1.1 为什么需要一个插件

  • webpack基础配置无法满足需求
  • 插件几乎能够任意更改webpack编译结果
  • webpack内部也是通过大量内部插件实现的

1.2 可以加载插件的常用对象

对象钩子
Compilerrun,compile,compilation,make,emit,done
CompilationbuildModule,normalModuleLoader,succeedModule,finishModules,seal,optimize,after-seal
Module FactorybeforeResolver,afterResolver,module,parser
Module
parserprogram,statement,call,expression
Templatehash,bootstrap,localVars,render

2.创建插件

webpack 插件由以下组成:

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

3. Compiler 和 Compilation

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

  • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境
  • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

4. 基本插件架构

  • 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的
  • 这个 apply 方法在安装插件时,会被 webpack compiler 调用一次
  • apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象

4.1 使用插件源代码

	if (options.plugins && Array.isArray(options.plugins)) {
		for (const plugin of options.plugins) {
			plugin.apply(compiler);
		}
	}

4.2 调用Compiler插件done钩子创建插件

(1)同步插件使用tap

class DonePlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.done.tap('DonePlugin', (stats) => {
            console.log('Hello ', this.options.name);
        });
    }
}
module.exports = DonePlugin;

(2)异步插件使用tapAsync

class DonePlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.done.tapAsync('DonePlugin', (stats, callback) => {
            console.log('Hello ', this.options.name);
            callback();
        });
    }
}
module.exports = DonePlugin;

4.3 使用插件

  • 要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例
const DonePlugin=require('./plugins/DonePlugin');
module.exports={
    entry: './src/index.js',
    output: {
        path: path.resolve('build'),
        filename:'bundle.js'
    },
    plugins: [
        new DonePlugin({name:'zfpx'})
    ]
}

最后在项目中实执行所有引用的plugin也都是在触发done钩子函数中执行的 源码地址

if (this.hooks.shouldEmit.call(compilation) === false) {
		const stats = new Stats(compilation);
		stats.startTime = startTime;
		stats.endTime = Date.now();

		this.hooks.done.callAsync(stats, err => {
		if (err) return finalCallback(err);
		return finalCallback(null, stats);
		});
		
		return;
}