webpack 从入门到放弃!( 六 )


钩子机制也特别容易理解,它有点类似于 Web 中的事件 。在整个工作过程会有很多环节,为了便于插件的扩展,几乎在每一个环节都埋下了一个钩子 。这样我们在开发插件的时候,通过往这些不同节点上挂载不同的任务,就可以轻松扩展的能力 。
具体有哪些预先定义好的钩子,我们可以参考官方文档的 API:
【webpack 从入门到放弃!】接下来,我们来开发一个自己的插件,看看具体如何往这些钩子上挂载任务 。
这里我的需求是,希望我们开发的这个插件能够自动清除打包结果中的注释,这样一来,我们的 .js 将更容易阅读,如下图所示:
在项目根目录下添加一个单独的 JS 文件 。
要求我们的插件必须是一个函数或者是一个包含 apply 方法的对象,一般我们都会定义一个类型,在这个类型中定义 apply 方法 。然后在使用时,再通过这个类型来创建一个实例对象去使用这个插件 。
所以我们这里定义一个类型,然后在这个类型中定义一个 apply 方法,这个方法会在启动时被调用,它接收一个对象参数,这个对象是工作过程中最核心的对象,里面包含了我们此次构建的所有配置信息,我们就是通过这个对象去注册钩子函数,具体代码如下:
// ./remove-comments-plugin.jsclass RemoveCommentsPlugin {apply (compiler) {console.log('RemoveCommentsPlugin 启动')// compiler => 包含了我们此次构建的所有配置信息}}
知道这些过后,还需要明确我们这个任务的执行时机,也就是到底应该把这个任务挂载到哪个钩子上 。
我们的需求是删除 .js 中的注释,也就是说只有当需要生成的 .js 文件内容明确过后才可能实施 。
那根据 API 文档中的介绍,我们找到一个叫作 emit 的钩子,这个钩子会在即将向输出目录输出文件时执行,非常符合我们的需求 。
我们回到代码中,通过对象的 hooks 属性访问到 emit 钩子,再通过 tap 方法注册一个钩子函数,这个方法接收两个参数:
根据 API 文档中的提示,这里我们在这个函数中接收一个对象参数,这个对象可以理解为此次运行打包的上下文,所有打包过程中产生的结果,都会放到这个对象中 。
我们可以使用这个对象中的属性获取即将写入输出目录的资源文件信息,它是一个对象,我们这里通过 for in 去遍历这个对象,其中键就是每个文件的名称,我们尝试把它打印出来,具体代码如下:
// ./remove-comments-plugin.jsclass RemoveCommentsPlugin {apply (compiler) {compiler.hooks.emit.tap('RemoveCommentsPlugin', compilation => {// compilation => 可以理解为此次打包的上下文for (const name in compilation.assets) {console.log(name) // 输出文件名称}})}}
完成以后,我们将这个插件应用到的配置中,然后回到命令行重新打包,此时打包过程就会打印我们输出的文件名称,代码如下:
我们再回到代码中,来打印一下每个资源文件的内容,文件内容需要通过遍历的值对象中的方法获取,具体代码如下:
// ./remove-comments-plugin.jsclass RemoveCommentsPlugin {apply (compiler) {compiler.hooks.emit.tap('RemoveCommentsPlugin', compilation => {// compilation => 可以理解为此次打包的上下文for (const name in compilation.assets) {// console.log(name)console.log(compilation.assets[name].source()) // 输出文件内容}})}}
回到命令行,再次打包,此时输出的文件内容也可以正常被打印 。
能够拿到文件名和文件内容后,我们回到代码中 。这里需要先判断文件名是不是以 .js 结尾,因为打包还有可能输出别的文件,而我们的需求只需要处理 JS 文件 。