webpack原理 (5)

插件是 webpack 生态系统的重要组成部分,为社区用户提供了一种强大方式来直接触及 webpack 的编译过程(compilation process)。插件能够 到在每个编译(compilation)中触发的所有关键事件。在编译的每一步,插件都具备完全访问 compiler 对象的能力,如果情况合适,还可以访问当前 compilation 对象。

自定义插件本质就是在webpack的编译过程的提供的生命周期钩子中,进行编码开发实现一些功能,在适当的时间节点做该做的事情,例如clean-webpack-plugin插件,就是在编译之前执行插件,将打包目录清空。

自制plugin

在实现自制插件之前,先了解一下webpack插件组成

一个JavaScript命名函数

在插件函数的prototype上定义一个apply方法

指定一个绑定到webpack自身的事件钩子

处理webpack内部实例的特定数据

功能完成后调用webpack提供的回调

webpack的生命周期钩子

Compiler 模块是 webpack 的支柱引擎,它通过 CLI 或 Node API 传递的所有选项,创建出一个 compilation 实例。它扩展(extend)自 Tapable 类,以便注册和调用插件。大多数面向用户的插件首,会先在 Compiler上注册。

hello word

根据官方文档实现一个hello word插件,可以简单的了解到plugin

// 1. 一个JavaScript命名函数 // 2. 在插件函数的 prototype 上定义一个apply方法 class HelloWordPlugin { // 3. apply 中有一个 compiler 形参 apply(compiler){ console.log('插件执行了'); // 4. 通过compiler对象可以注册对应的事件,全部的钩子都可以使用 // 注册一个编译完成的钩子, 一般需要将插件名作为事件名即可 compiler.hooks.done.tap('HelloWordPlugin', (stats) => { console.log('整个webpack打包结束了'); }) compiler.hooks.emit.tap('HelloWordPlugin', (compilation) => { console.log('触发emit方法'); }) } } module.exports = HelloWordPlugin

在webpack.config.js引入并使用

const HelloWordPlugin = require('./plugins/HelloWordPlugin') { // ... plugins:[ new HelloWordPlugin() ] }

npx webpack打包,可以查看插件的触发

>>> 插件执行了 >>> 触发emit方法 >>> 整个webpack打包结束了 HtmlWebpackPlugin

模仿实现HtmlWebpackPlugin插件的功能

html-webpack-plugin 可以将制定的html模板复制一份输出到dist目录下,自动引入bundle.js

实现步骤

编写一个自定义插件,注册 afterEmit 钩子

根据创建对象时传入的 template 属性来读取 html 模板

使用工具分析HTML,推荐使用 cheerio,可以直接使用jQuery API

循环遍历webpack打包的资源文件列表,如果有多个bundle就都打包进去

输出新生成的HTML字符串到dist目录中

const path = require('path') const fs = require('fs') const cheerio = require('cheerio') class HTMLPlugin { constructor(options){ // 插件的参数,filename、template等 this.options = options } apply(compiler){ // 1. 注册 afterEmit 钩子 // 如果使用done钩子,则需要使用stats.compilation.assets获取,而且会比 afterEmit 晚一些 compiler.hooks.afterEmit.tap('HTMLPlugin', (compilation) => { // 2. 根据模板读取html文件内容 const result = fs.readFileSync(this.options.template, 'utf-8') // 3. 使用 cheerio 来分析 HTML let $ = cheerio.load(result) // 4. 创建 script 标签后插入HTML中 Object.keys(compilation.assets).forEach(item => { $(`<script src="http://www.likecs.com/${item}"></script>`).appendTo('body') }) // 5. 转换成新的HTML并写入到 dist 目录中 fs.writeFileSync(path.join(process.cwd(), 'dist', this.options.filename), $.html()) }) } } module.exports = HTMLPlugin

注意 Compiler 和 Compilattion 的区别

compile: r对象表示不变的webpack环境,是针对webpack的

compilation: 对象针对的是随时可变的项目文件,只要文件有改动,compilation就会被重新创建

添加plugin功能

为webpack-theory添加plugin功能,只需在Compiler构造时,创建对应的钩子即可,webpack-theory只是负责定义钩子,并在适当的时间节点去触发,至于钩子的事件注册都是各个plugin自己内部去实现。

// tapable 的构造函数内部定义的钩子 this.hooks = { afterPlugins: new SyncHook(), beforeRun: new SyncHook(), run: new SyncHook(), make: new SyncHook(), afterCompiler: new SyncHook(), shouldEmit: new SyncHook(), emit: new SyncHook(), afterEmit: new SyncHook(['compilation']), done: new SyncHook(), } // 触发plugins中所有插件的apply方法, 并传入Compiler对象 if(Array.isArray(this.config.plugins)){ this.config.plugins.forEach(plugin => { plugin.apply(this) }); }

在合适的时机调用对应钩子的call方法即可,如需要传入参数,可以在对应的钩子中定义好需要传入的参数,call时直接传入

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdwzs.html