前言
plugin 顾名思义就是插件的意思,它在webpack的运行过程中起到了非常重要的作用。其实webpack 80%的代码都是基于plugin实现的,可以说它是一个汇集各种plugin的大集合,里面有各种各样的plugin分别实现不同的功能,plugin就是webpack的灵魂!
plugin的作用
plugin是运行在webpak打包过程中的某段逻辑,它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作。
常用的plugin:
html-webpack-plugin,这是一个生成html的插件,每次打包都会调用它来接收生成的静态资源,如下面的例子
- 新建一个项目名为plugin的目录,cd进入这个目录,执行初始化命令 npm init -y, 按回车键确认
- 在plugin目录建一个src目录,并且在src目录下建一个index.js作为入口文件,随便在index.js中写点内容
console.log('index.js')
- 安装插件
npm install webpack webpack-cli html-webpack-plugin clean-webpack-plugin -D
- 在plugin根目录下建一个webpack.config.js, 配置webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin(), // 清除dist目录
]
}
- 在package.json中配置打包命令"build": "webpack"
{
"name": "plugin",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9"
}
}
- 执行打包命令
npm run build
详细如下图
打包就会生成一个dist目录,下面有main.js与index.html
在这过程中我用到了两个plugin : html-webpack-plugin 与 clean-webpack-plugin,他们的作用相信大家都知道了。
那么它到底是怎么起到作用的呢?
前面我介绍过plugin的作用:
它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作。
接下来,我们就根据功能来模仿html-webpack-plugin来写一个MyHtmlWebpackPlugin体会一下plugin是怎么一回事。
首先,我们在写之前要清楚plugin是怎么写的,有什么要求,去官方文档https://webpack.js.org/api/plugins/ 与 https://webpack.js.org/contribute/writing-a-plugin/
查阅后原来plugin就是一个类,
它的主要的步骤如下:
- 编写一个JavaScript class
- 在class里面写一个apply方法,这个方法接收一个参数compiler,表示这次打包的上下文。
- 指定挂载的webpack事件钩子。
- 处理webpack内部实例的特定数据。
- 功能完成后调用webpack提供的回调。
编写插件之前要理解compiler和compilation两个对象,以及webpack生命周期的各个阶段和钩子。
然后我们开始写,还是在plugin这个项目里面写,在src目录下新建一个plugins的目录,在plugins目录下新建一个MyHtmlWebpackPlugin.js
写之前我们理清一下思路:
1、写一个class 里面有一个apply方法,接收一个compiler参数,开始写
class MyHtmlWebpackPlugin {
apply(compiler) {}
}
2、然后指定挂载的webpack事件钩子,在里面做我们要做的事情,我们要做什么事情?
我先查一下这个https://webpack.js.org/api/compiler-hooks/文档看一下有哪些钩子可以用,然后我们的插件的目的很明确,就是拿到打包后的资源,插入到html中,并且生成一个html到dist目录下。
3、所以接下来就是在对应的钩子干活了,在apply方法里面写这些逻辑。
class MyHtmlWebpackPlugin {
apply(compiler) {
// 在 emit 这个异步钩子里面拿到生成好的静态资源,第一个参数必须和我们的插件同名'MyHtmlWebpackPlugin'
compiler.hooks.emit.tapAsync('MyHtmlWebpackPlugin', (compilation, callback) => {
// 给一个默认的html
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MyIndex</title>
</head>
<body>
</body>
</html>
`
// 提取js文件
const scriptFile = '<script type="text/javascript" src="{}"></script>'
const scriptArr = []
for (let filename in compilation.assets) {
if (/\.js$/.test(filename)) {
const file = scriptFile.replace('{}', filename)
scriptArr.push(file)
}
}
// 把提取到js文件插入html里面去,返回一个新的html
const newHtml = html.replace('</body>', scriptArr.join('\n') + '</body>')
// 最后在compilation.assets下面挂载一个新的命名为MyIndex.html输出文件,里面有它的内容和内容的长度
compilation.assets['MyIndex.html'] = {
source: function () {
return newHtml;
},
size: function () {
return newHtml.length;
}
};
// callback这个回调方法必须要执行
callback();
});
}
}
// 导出这个plugin
module.exports = MyHtmlWebpackPlugin
4、 来试一下我们写的插件,把webpack.config.js中的html-webpack-plugin注释,换成我们写的MyHtmlWebpackPlugin
const path = require('path');
// const HtmlWebpackPlugin = require('html-webpack-plugin')
const MyHtmlWebpackPlugin = require('./src/plugins/MyHtmlWebpackPlugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
plugins: [
// new HtmlWebpackPlugin(),
new MyHtmlWebpackPlugin(),
new CleanWebpackPlugin(), // 清除dist目录
]
}
5、npm run build 结果如下
这样我们就模仿html-webpack-plugin写出一个类似的插件了。虽然还有其他功能没有实现,只实现了插入script标签的功能。
总结
plugin就是一个类,这个类里面有一个apply方法,我们在这个apply里面根据需求,在对应的钩子中注册我们的想要做的事件。以上是我的理解,如有不对请留言指出讨论,谢谢。
网友评论