写一个webpack插件

1,533 阅读4分钟

用问题去学习,用测试来验证。

一个webpack插件简单的例子(来自webpack官方文档)

1, 写一个class 类
2, 写一个apply方法
3, 写一个监听方法
2, 写一个处理逻辑

// A JavaScript class.
class MyExampleWebpackPlugin {
  // Define `apply` as its prototype method which is supplied with compiler as its argument
  apply(compiler) {
    // Specify the event hook to attach to
    compiler.hooks.emit.tapAsync(
      'MyExampleWebpackPlugin',
      (compilation, callback) => {
        console.log('This is an example plugin!');
        console.log('Here’s the `compilation` object which represents a single build of assets:', compilation);

        // Manipulate the build using the plugin API provided by webpack
        compilation.addModule(/* ... */);

        callback();
      }
    );
  }
}

compile?

看插件的时候,我就一直有个疑问,什么是compile?

什么是compile?

compile就是一个webpack实例化的对象,包括webpack打包的所有的信息,所有的上下文,包括内置的函数和webpack配置的信息,总之webpack执行过程的所有的信息。

当我们定义的一个plugin,比如是名字是A的插件的时候,首先是定义了一个类,这个类有一个apply方法,然后方法里边监听了webpack的打包过程中的事件。 使用A的时候,把A的实例传入webpack.config.js的pulugins里边。

当执行webpack的时候,webpack就会执行

A.apply(compile)

把compile对象传给A的apply方法。

什么是compilation?

compilation 是每次编译的时候创建的全局变量,包含所有的上下文,同时可以取到compile对象

写一个插件

上面说了这么多,还不如一次实战,下面就开始写一个插件。

背景描述

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  
  <srcipt src="1.js"> </script>
  <srcipt src="2.js"> </script>
</body>
</html>

上面是我们的模板html文件,现在有个问题,当通过dllPlugin和html-webpack-plugin进行打包后,会变成这样,

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  
  <srcipt src="1.js"> </script>
  <srcipt src="2.js"> </script>
  <srcipt src="dll.js"> </script>
  <srcipt src="main.js"> </script>
</body>
</html>

其实上面我们的1.js和2.js一般都是代码统计js文件,现在想写个webpack插件,让打包后的文件,插入到代码统计文件的前面,也就是如下这样:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  
  <srcipt src="dll.js"> </script>
  <srcipt src="main.js"> </script>
  <srcipt src="1.js"> </script>
  <srcipt src="2.js"> </script>
</body>
</html>

分析

这里应该很简单,因为html-webpack-plugin肯定是拿到我们的js文件,然后直接插入到我们的模板文件中去的,所以我们要做的就是在html-webpack-plugin插入之前,我们把html-webpack-plugin想要插入的内容给改变,把我们想添加的给添加进去,然后再让html-webpack-plugin工作。

代码

  • 1 定义我们的插件,代码很简单,没什么好说的。
const HtmlWebpackPlugin = require("html-webpack-plugin");

class MyPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.compilation.tap("MyPlugin", compilation => {
      console.log("The compiler is starting a new compilation...");
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(
        "MyPlugin",
        (data, cb) => {
          const paths = this.options.arr1;
          console.log(111111);
          console.log(data.assets);
          for (let i = paths.length - 1; i >= 0; i--) {
            data.assets.js.push(paths[i]);
          }
          cb(null, data);
        }
      );
    });
  }
}
module.exports = MyPlugin;

  • 2 使用
new MyPlugin({
      arr1: [
        "https://g.alicdn.com/de/prismplayer/2.5.1/aliplayer-min.js",
        "https://hm.baidu.com/hm.js?1ea7b7fd0d7259a472147a1ffd544938"
      ]
    }),
    new HtmlWebpackPlugin({
      template: "./index.ejs",
      filename: "../index.html",
      inlineSource: "manifest",
      inject: "body"
    }),

遇到的问题

  • 1  html-webpack-plugin的版本刚开始太低,因为以前安装的是3.2.0的版本,导致报 HtmlWebpackPlugin.getHooks 不存在的错误

解决办法: 卸载html-webpack-plugin旧版本,安装最新的html-webpack-plugin版本

  • 2  "dependency" is not a valid chunk sort mode
new HtmlWebpackPlugin({
            chunksSortMode: 'dependency'
        }),

解决办法: 去掉chunksSortMode: 'dependency'

  • 3  Please ensure that html-webpack-plugin was placed before html-replace-webpack-plugin in your Webpack config if you were working with Webpack 4.x!

解决办法:明明已经放在前面了,不知道为什么一直报这个错误。后来一想,可能和版本有关系,因为升级了html-webpack-plugin,所以html-replace-webpack-plugin也得升级,所以把html-replace-webpack-plugin升级到2.5.6,就解决了。

new HtmlWebpackPlugin({
      template: "./index.ejs",
      filename: "../index.html",
      inlineSource: "manifest",
      inject: "body"
    }),

    new HtmlReplaceWebpackPlugin([
      {
        pattern: '<link rel="stylesheet" href="/load.css" />',
        replacement: `<style type="text/css">1111</style>`
      }
    ]),