一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情。
webpack现在已经是一个非常常见的工具了,里面常用的有很多,其中需要注意的是:
-
module, 通常一个module对应一个文件
-
chunk,代码块,一个chunk由多个模块组成,用于代码合并与分割
-
loader,模块转换器,把模块原有的内容按照需求转成新的内容
-
plugin, 扩展插件,可以在webpack构建流程的特定时机改变构建结果,或者产生一些副作用
整体来看,webpack是基于事件流来实现的,核心的概念就是插件机制。
插件由什么构成?
- 一个js函数承载所有的内容
- 在原型上定义的一个apply方法,插件安装的时候会被执行一次
- 指定一个钩子,用来特定的时机做特定的事情
- 对webpack实例内部做一些处理
- 可以调用webpack提供的回调函数
比如我们要实现一个编译完成后打印hello world的功能,可以这么写:
Class HW Class {
apply(compiler) {
compiler.hooks.done.tap(‘hw plugin’, () => console.log(‘hello world’))
}
}
其中的compiler包含了webpack运行流程包含的webpack所有信息,options, loaders, plugins等,这个对象在启动的时候被实例化,全局唯一。
我们需要在compiler暴露的钩子上指明我们的tap配置,可以在指定的位置触发你所编写的回调函数。
那么基于核心tapable库的事件机制该怎么使用。我们来举个例子,首先初始化:
Const { SyncHook } = require(‘tapable’);
Const hook = new SyncHook([‘name’]);
然后用这个hook实例去使用,分两步:
- 注册监听: hook.tap
- 触发监听: hook.call(…param)
看起来跟我们传统的事件机制的区别,就是事件的触发和监听都是直接关联到一个tapable实例上的,所以只要确保能拿到这个实例就可以。
如果我们自己实现一套事件的触发机制,可以这么实现:
- 先保存到全局的对象上,如{hookNameA: cb, hookNameB: cb2}
- 然后在触发的地方先生成hook,然后循环遍历全局对象,找到跟这个hook对应的回调, hook.tap(cb) hook.tap(cb2),然后再直接hook.call()
那么这样有什么好处呢,如果有多个事件,就可以通过自然地串联起来,因为tapable支持很多的类型的监听钩子。
另外需要注意的是compilation这个东西,它包含了模块资源、编译生成资源、变化的文件等。每当检测到一个文件变化,一个新的compilation就会生成,代码一次单独的编译。它也有不同的钩子暴露给开发者去使用。