必须知道的plugin基础知识,流程:
plugin
plugin是流水线,在执行过程中不同时间段执行扩展功能
Tapable 注册 tap,tapAsync,tabPromise
-
tab 可以注册同步或异步钩子
-
tabAsync 回调方式注册异步钩子
-
tabPromise Promise方式注册异步钩子,返回Promise
Compiler 和 Compilation
-
compiler 对象保持着完整的webpack环境配置,可以访问到loader,plugin等
-
主要使用以下属性:
1、compiler.options 可以访问本次启动的webpack的所有配置文件,包括但不限于 loaders,entry,plugin等完整配置信息
2、compiler.inputFileSystem 和 compiler.outputFileSystem 可以进行文件操作,相当于nodejs中 fs
3、compiler.hooks 可以注册 tabpable 的不同种类Hooks,从而可以在compiler生命周期中注入不同逻辑
-
compilation 代表一次资源构建,compilation实例能够访问所有模块和他的依赖
-
主要使用以下属性:
1、compilation.modules 可以访问所有模块,打包的每一个文件都是一个模块
2、compilation.chunks chunk即是多个modules组成而来的一个代码块,入口文件引入的资源组成一个chunk,通过代码分割的模块又是另一个chunk
3、compilation.assets 可以访问本次打包生成的所有文件结果
4、compilation.hooks 可以注册tabpale的不同种类hooks,用于在compilation编译模块阶段进行逻辑添加以及修改
node 调试方式
package.json 中
{
"scripts":{
"debug": "node --inspect-brk ./node_modules/webpack-cli/bin/cli.js"
}
}
--inspect-brk 在第一行停下来方便调试,直接命令行运行 npm run debug
打开浏览器,控制台就看到 nodejs 调试按钮了
例子
BannerWebpackPlugin 最终在js和css文件头加上注释
class BannerWebpackPlugin {
constructor(options={}) {
this.options = options
}
apply(compiler) {
compiler.hooks.emit.tapAsync(
"BannerWebpackPlugin",
(compilation, callback) => {
// 1、获取即将输出的资源:compilation.assets
// 2、过滤只保留js和css文件,图片字体等跳过
// 3、遍历剩下资源,添加注释
const assets = Object.keys(compilation.assets).filter((assetPath) =>
/\.(css|js)/.test(assetPath)
);
assets.forEach((asset) => {
const source = compilation.assets[asset];
const content = "/*添加的前缀*/" + source;
compilation.assets[asset] = {
// 最终资源输出时,调用source方法,返回值就是最终资源具体内容
source() {
return content;
},
// 资源大小
size() {
return content.length;
},
};
});
callback();
}
);
}
}
module.exports = BannerWebpackPlugin;
CleanWebpackPlugin 清空目标文件夹
class CleanWebpackPlugin {
constructor() {}
apply(compiler) {
// 1、在打包输出前注册 emit
// 2、获取打包目录
// 3、通过fs删除目标目录
const outputPath = compiler.options.output.path;
const fs = compiler.outputFileSystem;
compiler.hooks.emit.tap("CleanWebpackPlugin", (compilation) => {
this.removeFiles(fs, outputPath);
});
}
removeFiles(fs, filePath) {
// 删除目录下所有文件
// 1、读取当前目录下所有资源
// 2、遍历一个一个删除
// 删除空目录
const files = fs.readdirSync(filePath);
files.forEach((file) => {
const fullPath = `${filePath}/${file}`;
const fileStat = fs.statSync(fullPath);
if (fileStat.isDirectory()) {
this.removeFiles(fs, fullPath);
fs.rmdirSync(fullPath);
} else {
fs.unlinkSync(fullPath);
}
});
}
}
module.exports = CleanWebpackPlugin;
AnalyzeWebpackPlugin 统计打包文件大小
class AnalyzeWebpackPlugin {
constructor() {}
apply(compiler) {
compiler.hooks.emit.tap("AnalyzeWebpackPlugin", (compilation) => {
const assets = Object.entries(compilation.assets);
let content = `| 资源名称 | 资源大小 |
| --- | --- |`;
assets.forEach(([filename, file]) => {
content += `\n|${filename}|${Math.ceil(file.size() / 1024)}|`;
});
compilation.assets["analyze.md"] = {
source() {
return content;
},
size() {
return content.length;
},
};
});
}
}
module.exports = AnalyzeWebpackPlugin;
InlineHtmlWebpackPack 将runtime文件删除,直接在index.html文件中内联
const HtmlWebpackPlugin = require("safe-require")("html-webpack-plugin");
class InlineChunkWebpackPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"InlineChunkWebpackPlugin",
(compilation) => {
// 1、获取html-webpack-plugin的hooks
const hooks = HtmlWebpackPlugin.getHooks(compilation);
// 2、注册 html-webpack-plugin 的hooks -> alterAssetTagGroups
hooks.alterAssetTagGroups.tap("InlineChunkWebpackPlugin", (assets) => {
// 3、从里面将script的runtime文件,变成inline script
assets.headTags = this.getInlineChunk(
assets.headTags,
compilation.assets
);
assets.bodyTags = this.getInlineChunk(
assets.bodyTags,
compilation.assets
);
});
// 4、删除runtime文件
hooks.afterEmit.tap("InlineChunkWebpackPlugin", () => {
Object.keys(compilation.assets).forEach((filepath) => {
if (/runtime(.*)\.js$/.test(filepath)) {
delete compilation.assets[filepath];
}
});
});
}
);
}
getInlineChunk(tags, assets) {
/**
目前:
[
{
tagName: 'script',
voidTag: false,
meta: { plugin: 'html-webpack-plugin'},
attributes: {defer: true, type: undefined, src: 'js/runtime~main.js'}
}
]
修改为:
[
{
tagName: 'script',
innerHTML : 'runtime文件内容',
closeTag : true
}
]
*/
return tags.map((tag) => {
if (tag.tagName !== "script") return tag;
// 获取文件路径
const filepath = tag.attributes.src;
if (!filepath) return tag;
if (!/runtime(.*)\.js$/g.test(filepath)) return tag;
return {
tagName: "script",
innerHTMl: assets[filepath].source(),
closeTag: true,
};
});
}
}
module.exports = InlineChunkWebpackPlugin;