手写一个webpack plugin

70 阅读2分钟

在文件夹中新建一个js文件:

class FileListPlugin {
  static defaultOptions = {
    outputFile: "assets.md",
  };

  // 需要传入自定义插件构造函数的任意选项
  //(这是自定义插件的公开API)
  constructor(options = {}) {
    // 在应用默认选项前,先应用用户指定选项
    // 合并后的选项暴露给插件方法
    // 记得在这里校验所有选项
    this.options = { ...FileListPlugin.defaultOptions, ...options };
  }

  apply(compiler) {
    const pluginName = FileListPlugin.name;
    console.log(pluginName, "pluginname");
    // webpack 模块实例,可以通过 compiler 对象访问,
    // 这样确保使用的是模块的正确版本
    // (不要直接 require/import webpack)
    const { webpack } = compiler;
    // console.log(webpack, "webpack");
    // console.log(compiler, "compiler");
    // Compilation 对象提供了对一些有用常量的访问。
    const { Compilation } = webpack;
    // console.log(webpack.sources, "webpack.sources");

    // RawSource 是其中一种 “源码”("sources") 类型,
    // 用来在 compilation 中表示资源的源码
    const { RawSource } = webpack.sources;

    // 绑定到 “thisCompilation” 钩子,
    // 以便进一步绑定到 compilation 过程更早期的阶段
    compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
      // 绑定到资源处理流水线(assets processing pipeline)
      compilation.hooks.processAssets.tap(
        {
          name: pluginName,

          // 用某个靠后的资源处理阶段,
          // 确保所有资源已被插件添加到 compilation
          stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
        },
        (assets) => {
          // "assets" 是一个包含 compilation 中所有资源(assets)的对象。
          // 该对象的键是资源的路径,
          // 值是文件的源码

          // 遍历所有资源,
          // 生成 Markdown 文件的内容
          //   console.log(assets, "assets");
          const content =
            "# In this build:\n\n" +
            Object.keys(assets)
              .map((filename) => `- ${filename}`)
              .join("\n");

          // 向 compilation 添加新的资源,
          // 这样 webpack 就会自动生成并输出到 output 目录
          compilation.emitAsset(
            this.options.outputFile,
            new RawSource(content)
          );
        }
      );
    });
  }
}

module.exports = { FileListPlugin };

使用:

const { FileListPlugin } = require("./src/MyPlugins/file-list-plugin");

plugins: [
    // 添加插件,使用默认选项
    new FileListPlugin(),
    ]

插件的核心

本质是一个js的类 那么类可以有的属性 方法 等 都可以用在编写插件中; 必须要有的是apply(compiler),在webpack源码中会使用该方法并传入compiler对象; 附上源码:

const createCompiler = rawOptions => {
	const options = getNormalizedWebpackOptions(rawOptions);
	applyWebpackOptionsBaseDefaults(options);
	const compiler = new Compiler(
		/** @type {string} */ (options.context),
		options
	);
	new NodeEnvironmentPlugin({
		infrastructureLogging: options.infrastructureLogging
	}).apply(compiler);
	if (Array.isArray(options.plugins)) {
		for (const plugin of options.plugins) {
			if (typeof plugin === "function") {
				plugin.call(compiler, compiler);
			} else if (plugin) {
				plugin.apply(compiler);//此处调用了apply 传入了compiler
			}
		}
	}
	applyWebpackOptionsDefaults(options);
	compiler.hooks.environment.call();
	compiler.hooks.afterEnvironment.call();
	new WebpackOptionsApply().process(options, compiler);
	compiler.hooks.initialize.call();
	return compiler;
};