一、plugin是什么?
plugin插件,能够赋予webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,他们会被指定在webpack的不同生命周期中实现某个具体的功能,贯穿了webpack整个编译周期。
其目的也是为了解决loader无法解决的事情
配置方式
一般情况下,通过配置对象的plugins属性,传入new 出来对实例对象。
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 访问内置的插件
module.exports = {
...
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
};
二、plugin特性
其本质是一个具有apply方法的javascript对象
apply方法会被webpack compiler调用,并且在整个生命周期阶段都可以访问complier对象。
compiler 钩子
Compiler
模块是 webpack 的主要引擎,它通过 CLI 或者 Node API 传递的所有选项创建出一个 compilation 实例。 它扩展(extends)自Tapable
类,用来注册和调用插件。 大多数面向用户的插件会首先在Compiler
上注册,他贯穿了整个webpack的生命周期阶段。
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, (compilation) => {
console.log('webpack 构建过程开始!');
});
}
}
module.exports = ConsoleLogOnBuildWebpackPlugin;
关于整个编译生命周期钩子,有如下:
- entry-option :初始化 option
- run
- compile: 真正开始的编译,在创建 compilation 对象之前
- compilation :生成好了 compilation 对象
- make 从 entry 开始递归分析依赖,准备对每个模块进行 build
- after-compile: 编译 build 过程结束
- emit :在将内存中 assets 内容写到磁盘文件夹之前
- after-emit :在将内存中 assets 内容写到磁盘文件夹之后
- done: 完成所有的编译过程
- failed: 编译失败的时候
三、常用Plugin
HtmlWebpackPlugin
在打包结束后,生成一个html文件,将打包好的js模块引入到该html中
// webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
title: "My App",
filename: "app.html",
template: "./src/html/index.html"
})
]
};
<!--./src/html/index.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><%=htmlWebpackPlugin.options.title%></title>
</head>
<body>
<h1>html-webpack-plugin</h1>
</body>
</html>
在html中,可以通过 <%=htmlWebpackPlugin.options.XXX%> 的方式获取配置值
clean-webpack-plugin
删除(清理)构建目录
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...,
new CleanWebpackPlugin(),
...
]
}
mini-css-extract-plugin
提取 CSS
到一个单独的文件中
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...,
module: {
rules: [
{
test: /\.s[ac]ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'sass-loader'
]
}
]
},
plugins: [
...,
new MiniCssExtractPlugin({
filename: '[name].css'
}),
...
]
}
四、自定义Plugin - 测试
//1. webpack加载webpack.config.js配置项,解析到plugins属性使,执行new TestPlugin,执行插件constructor
//2. webpack创建compiler对象(webpack一次执行创建一个)
//3. 遍历所有plugin插件,调用apply方法
//4. 执行剩下编译流程(触发各hook事件)
class TestPlugin {
constructor() {
console.log("执行constructor")
}
apply(compiler) {
console.log("执行apply方法");
debugger;
// console.log(compiler);
//tap同步钩子
//environment:在编译器准备环境时调用,时机在配置文件初始化插件之后
compiler.hooks.environment.tap("TestPlugin", () => {
console.log("TestPlugin environment");
})
//entryOption:entry被处理之后调用
compiler.hooks.entryOption.tap("TestPlugin", (content,entry) => {
console.log("TestPlugin entryOption");
})
//-------emit:异步串行---------------------------------
//emit异步钩子同步注册
//在资源asset到output之前执行
compiler.hooks.emit.tap("TestPlugin", (compilation) => {
console.log("TestPlugin tap异步串行1");
})
//tapAsync:异步操作
compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {
setTimeout(() => {
console.log("TestPlugin tapAsync异步串行2");
callback();//执行完毕,向下传递
}, 1000)
})
compiler.hooks.emit.tapPromise("TestPlugin", (compilation) => {
return new Promise((res, rej) => {
setTimeout(() => {
if (true) {
res("TestPlugin tapPromise222")
console.log("TestPlugin tapPromise异步串行3");
} else {
rej('promise error');
}
}, 1000)
})
})
//----------------------------------------------
//-------make:异步并行--------------------------------
compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
compilation.hooks.seal.tap("TestPlugin", () => {
console.log("TestPlugin seal111");
})
setTimeout(() => {
console.log("异步并行: makeAsync111");
callback();//向下传递
}, 3000)
})
compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
setTimeout(() => {
console.log("异步并行: makeAsync222");
callback();//向下传递
}, 1000)
})
compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
setTimeout(() => {
console.log("异步并行: makeAsync333");
callback();//向下传递
}, 2000)
})
//----------------------------------------------
}
}
module.exports = TestPlugin;
//webpack.config.js
const TestPlugin = require('./plugin/test-plugin')
module.exports = {
//......
plugins: [
new TestPlugin()
]
}
异步串行:从上往下依次执行
异步并行:同时执行,谁先执行完谁先输出
如果想要查看complier和complition对象配置项可以查看以下文章