webpack的源码里,大概有80%都是基于Plugin的机制编写的,webpack 自身的多数功能都使用Plugin这个插件接口。这个插件接口使 webpack 变得极其灵活。Plugin可以说是webpack的灵魂
Plugin的核心机制是发布订阅的设计模式
,代码之间的执行是通过事件来驱动的
我们来试着写一个Plugin
- 我们先来创建一个项目
mkdir plugin // 创建一个文件夹
npm init -y //初始化项目,生成package.json文件,package.json记录项目的信息,以及依赖包
mkdir src //根目录下创建src文件夹,并写入任意代码
npm install webpack webpack-cli --save-dev // 安装 webpack
touch webpack.config.js // 创建webpack配置文件并配置
-
src下创建index.js
- 配置
webpack.config.js
- 然后安装webpack
npm install webpack webpack-cli --save-dev
-
之后配置打包命令
- 运行打包命令
npm run build
,至此没有问题
现在我们的需求是,当整个打包过程结束的时候,希望生成一个版权文件,比如叫copyright.txt
,里边写一些版权信息,那么我们要怎么写这个插件呢?
- 创建文件夹
plugins
- 创建
copyright-webpack-plugin.js
,(插件通常都是这种格式的名称) -
loader本质上是一个方法,而插件本质上是一个类
-
我们先在插件里打印一句话,看看这个插件能不能用。插件写好后,我们要使用这个插件
-
然后我们运行打包,发现我们的插件是可以正常使用的
- 当然了,我们在new一个插件实例的时候,还可以传递一些参数,然后我们可以在
constructor
里接受这些参数
- 接受参数就是
constructor
的作用,如果不需要接受参数,也可以不写constructor
-
apply
里的compiler
参数,指的是webpack的实例,这个实例里存储了webpack相关的各种配置文件和打包过程等一系列内容,我们可以在webpack官网看到它的相关信息
-
在webpack的打包过程中,某些时刻,要做一些事情,具体有哪些时刻呢,compiler的hooks定义了一些具体的时刻值(类似于vue的钩子函数)
- 比如我们常用的
emit
钩子,它执行的时刻是生成资源到 output 目录之前,在我们的例子里就是当我们打包结束后,准备把东西放在dist目录的时候,我们就用到了emit
,它是异步时刻(AsyncSeriesHook)
tapAsync 函数接收两个参数,第一个参数是我们的插件名称,第二个参数是一个箭头函数,这个函数接收两个参数,第一个是compilation,第二个参数是cb回调,当我们调用tapAsync 异步函数的时候,一定要手动执行一下cb回调 ,compilation存放的是跟这一次打包相关的内容
- 然后我们运行打包命令
npm run build
,我们写的插件就帮我们生成了我们想要的版权文件
- 我们再来看一个同步的时刻
compile
,它代表的时刻是 `一个新的编译(compilation)创建之后,钩入(hook into) compiler。
class CopyRightWebpackPlugin {
apply(compiler) {
// compile 是同步的时刻,这个时候,不需要传入cb参数
compiler.hooks.compile.tap('CopyRightWebpackPlugin', (compilation) => {
console.log('compiler')
})
// emit 是异步的时刻,这个时候,需要传入cb参数,并手动执行
compiler.hooks.emit.tapAsync('CopyRightWebpackPlugin',(compilation, cb) => {
compilation.assets['copyright.txt'] = {
source: function () {
return 'copyright by LEE YANG'
},
size: function () {
return 21
}
}
cb();
})
}
}
module.exports = CopyRightWebpackPlugin
-
运行打包命令,发现执行了
我们在写插件的时候,怎么知道compilation
下边有一个assets呢?
-
我们先新配置一个命令
- 这两个命令运行的时候,做的事情是 一样的。那为什么我们还要多此一举写一个
debug
命令呢? 是因为当我们显式的用node去执行webpack.js
这个文件的时候,是因为我们可以在node运行webpack.js的时候,传递一些node的参数进去。
- 第一个参数
--inspect
意思是,我要开启node的调试工具,第二个参数--inspect-brk
的意思是,我在运行webpack.js做调试的时候,我在webpack命令执行的时候,它的第一行上面打一个断点 - 然后我们运行
npm run debug
,控制台告诉我们,Debugger已经被开启了
- 然后我们怎么做node的调试呢?我们打开chrome浏览器,打开https://webpack.js.org/,然后点击控制台,当我们开启了node的调试工具后,我们可以看到一个图标
-
我们点击图标 ,就会自动跳出这样一个窗口
-
这个弹窗里的代码,起始就是webpack的代码,它在第一行打了一个断点,我们可以点击按钮,让打包代码一步一步往下执行,我们直接让代码执行完,然后看下我们的打包过程,已经结束
- 如果我们在做插件调试的时候,想去看
compliation
里都有什么参数,我们可以在这里打一个断点
-
然后我们再运行debug命令,我们可以看到debugger又开启了,我们就可以再打开浏览器,打开控制台
-
然后我们可以点击图标让默认的断点,直接执行过去
-
默认断点执行过去之后,进入了下一个断点,也就是我们在插件代码里手写的断点这里
- 我们想看
compliation
里都有什么参数,有两种方法,第一个就是直接把鼠标放到compliation
上
-
还有一种方式就是通过右侧提供给我们的工具 watch,然后在watch里添加我们想要看的变量
- 这样我们就可以通过node的调试工具,来帮我们去看到在打包的插件里,我们用到的一些变量,到底是一些什么样的形式,实际上我们在编写webpack插件的时候,就是基于node的调试工具编写的。