搞定 webpack 的 plugin 不在话下

第 9 阶段:搞懂、搞透前端构建第 4 

上一节内容我们实现了一个 loader 从使用 loader 到实现 loader · webpack,这节内容我们讲讲 webpack 中另一个关键概念 plugin。plugin 就是一个插件,它的作用就是进行功能扩展。比如手机中的 App 其实也是一个插件,它基于手机已有的功能进行扩展;VSCode(第1天:开篇词,开发环境准备) 中强大的插件系统,也是基于 VSCode 已有的功能进行扩展。

同样, webapck 的插件系统也类似,当它对文件进行打包时会经过各种阶段,每一阶段发生时,它都会通知插件干了什么,插件本身可以获取到 webpack 整个执行环境的「方法和数据」,有了这些方法和数据,插件可以做它想要做的事情。

上面从宏观的角度分析了什么是插件,但真正的难点是实现一个插件,本文基于 webpack4.43.0 版本实现插件。我们实现一个 CleanPlugin,作用是打包之前清除先前打包的文件,也就是说每次打包时都会先把 dist(加入 output 为 dist) 目录中所有的文件删除。   

1、创建一个项目 day4-plugin,目录结构如下:

 index.html HTML 页面模板:

index.js 动态创建一个 p 标签,如果看不懂这个函数可以前往 让 JavaScript 文件代码相互独立

2、其中 webpack.config.js 配置如下,我创建了两个插件,从配置中可以看到插件的本质是一个类,只需要把实例放到 plugins 这个数组中即可:

3、在 package.json 中配置 npm script:

4、在根目录下执行 npm run build 进行打包:

5、打包成功后打开 index.html 文件,动态创建的内容显示出来了:

整个项目是跑起来了,我们看看 plugin 的本质是什么,打开 plugins/cleanPlugin.js,发现红色小箭头指向的内容我们都不懂,没关系,这是我们今天要掌握的内容:

创建 plugin 需要下面几个步骤:

1、定义一个 JavaScript 类(class CleanPlugin)或者函数;

class ClearPlugin {
  constructor(option) {
    console.log('ClearPlugin constructor called');
  }
}

2、需要定义一个 apply 方法;

当插件被安装到 webpack 时,会调用 plugin 实现的函数 apply,这个方法只会被调用一次,携带一个参数 compiler,你可以把它看做是 webpack 的实例,或者是 webpack 的的大管家,包含了 webpack 中所有的数据和方法。

class ClearPlugin {
  constructor(option) {
    console.log('ClearPlugin constructor called');
  }
  apply(compiler) {
    // 
  }
}

有关 comilper 的源码,可以查看:
https://github.com/webpack/webpack/blob/master/lib/Compiler.js

有几个概念需要特别留意下,比如 hooks,通过这个属性,可以监听到在整个编译过程中 webpack 发出的事件,hook 是 webpack 设计的核心概念。我们打印一下 compiler.hooks,可以看到有非常多的事件可以 hook:

3、指定 hook 来「钩住」你想要的事件;

插件的核心是能够监听的 webpack 整个编译过程中产生事件,compiler 有一个属性 hooks,这里保存了所有能够被 hook 的事件:

我们来钩一个 beforeRun 的事件,beforeRun 其实是 AsyncSeriesHook 对象,关于这些 hook 对象我们将单独开一节内容来讲:

const hooks = compiler.hooks;
console.log(Object.keys(hooks));
compiler.hooks.beforeRun.tap('clean-plugin', () => {
  console.log('webpack beforeRun');
});

4、处理 webpack 内部提供的数据;

官方提供了一个特别有意思的例子,在打包结果中添加一个编译文件,实现代码如下,需要注意一点这里使用的是 tapAsync:

hooks.emit.tapAsync('clean-plugin', (compilation, callback) => {
  // Create a header string for the generated file:
  var filelist = 'In this build:\n\n';


  // Loop through all compiled assets,
  // adding a new line item for each filename.
  for (var filename in compilation.assets) {
    filelist += '- ' + filename + '\n';
  }


  // Insert this list into the webpack build as a new file asset:
  compilation.assets['filelist.md'] = {
    source: function() {
      return filelist;
    },
    size: function() {
      return filelist.length;
    }
  };


  callback();
});

5、当处理的任务结束时,要给 webpack 一个回调告诉 webpack 我完事了;

回到我们的 CleanPlugin 中,我们的目的就是在 webpack 开始编译时把 output 目录下的文件清空。我这里借助了一个工具库 del,需要通过 npm install -S del 来安装这个库,然后用这个库对文件进行删除操作,具体实现如下:

const del = require("del");
// 1、定义一个 JavaScript 类(class CleanPlugin)或者函数;
class ClearPlugin {
  constructor(option) {
    console.log('ClearPlugin constructor called');
  }
  // 2、需要定义一个 apply 方法;
  apply(compiler) {
    const hooks = compiler.hooks;
    if (!compiler.options.output || !compiler.options.output.path) {
      // 没有配置输出文件,什么也不干
      return;
    }
    this.outputPath = compiler.options.output.path;
    // 3、指定 hook 来「钩住」你想要的事件;
    hooks.emit.tapAsync('clean-plugin', (compilation, callback) => {
      // 4、处理 webpack 内部提供的数据;
      const stats = compilation.getStats();
      if (stats.hasErrors()) {
        // 有错误不能删除文件
        return;
      }
      // 开始删除文件
      (async () => {
        await del([this.outputPath]);
        // 5、当处理的任务结束时,要给 webpack 一个回调告诉 webpack 我完事了;
        callback();
      })();
    });
  }
}

实现完以后,我们在 dist 文件夹下随便创建几个文件,我创建了 suyan.js:

当执行 npm run build 时,suyan.js 这个文件将被删除。

到此,今天的内容就结束了,从使用 plugin 到自己实现一个 plugin。本节代码:https://github.com/lefex/FE/tree/master/webpack。


最后,诚挚邀请你来思考下面的问题并打卡:

1、平时工作中你用到了哪些 plugin,它们的作用是啥?

大家加油!


推荐阅读:

从使用 loader 到实现 loader · webpack

https://webpack.js.org/contribute/writing-a-plugin/#creating-a-plugin

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值