webpack 自定义 plugin ?
plugin 的本质 类 (构造函数)
1 重要概念 tapable 类
const {
SyncHook, // 同步 执行
SyncBailHook, // 同步执行,但是一旦有返回值,就执行退出,不再继续执行其他
AsyncParallelHook, // 异步 并行执行
AsyncSeriesBailHook, // 异步 串行执行
} = require('tapable');
class Lesson {
constructor() {
// 初始化 hook容器
this.hooks = {
// 同步钩子,任务会依次执行
// go: new SyncHook(['address']),
go: new SyncBailHook(['address']), // 一旦有返回值,就不会再往下执行了
// 异步hooks
// AsyncParallelHook 异步并行
// leave: new AsyncParallelHook(['name', 'age']),
leave: new AsyncSeriesBailHook(['name', 'age']),
};
}
tap() {
// 往hooks 容器中注册事件/ 添加回调函数
this.hooks.go.tap('class001', (address) => {
console.log('class001', address);
return 111;
});
this.hooks.go.tap('class002', (address) => {
console.log('class002', address);
});
this.hooks.leave.tapAsync('class003', (name, age, cb) => {
setTimeout(() => {
console.log('class003', name, age);
cb();
}, 2000);
});
this.hooks.leave.tapPromise('class004', (name, age) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log('class004', name, age);
resolve();
}, 1000);
});
});
}
start() {
this.hooks.go.call('c318');
this.hooks.leave.callAsync('zh', '18', () => {
// 代表所有leave容器中的函数触发完了,才触发
console.log('end');
});
}
}
const l = new Lesson();
l.tap();
l.start();
##### 2 complier 类
拥有各种钩子函数
```js
class Plugin1 {
apply(complier) {
complier.hooks.emit.tap('Plugin1', () => {
console.log('emit 111111111111');
});
complier.hooks.afterEmit.tap('Plugin1', () => {
console.log('emit after 111111111111');
});
}
}
module.exports = Plugin1;
3 compilation 类 (通过compiler.hooks.thisCompilation 获取),可对文件进行操作
const fs = require('fs');
const util = require('util');
const path = require('path');
const { RawSource } = require('webpack-sources');
// 将fs . readFile 的异步方法,改变成同步的写法
const readFile = util.promisify(fs.readFile);
class Plugin2 {
apply(compiler) {
// 初始化compilation 的钩子
compiler.hooks.thisCompilation.tap('Plugin2', (compilation) => {
// debugger;
// console.log(compilation);
compilation.hooks.additionalAssets.tapAsync('Plugin2', async (cb) => {
const content = 'debug';
// 往要输出资源中,添加一个a.txt
compilation.assets['a.txt'] = {
size() {
return content.length;
}, // 文件大小
source() {
return content;
}, // 文件内容
};
const data = await readFile(path.resolve(__dirname, '../b.txt'));
compilation.assets['b.txt'] = new RawSource(data);
compilation.emitAsset('c.txt', new RawSource(data));
cb();
});
});
}
}
module.exports = Plugin2;
自定义插件 - CopyWebpackPlugin
const path = require('path');
const fs = require('fs');
const {promisify} = require('util')
const { validate } = require('schema-utils');
const globby = require('globby');
const webpack = require('webpack');
const schema = require('./schema.json');
const { Compilation } = require('webpack');
const readFile = promisify(fs.readFile);
const {RawSource} = webpack.sources
class CopyWebpackPlugin {
constructor(options = {}) {
// 验证options是否符合规范
validate(schema, options, {
name: 'CopyWebpackPlugin'
})
this.options = options;
}
apply(compiler) {
// 初始化compilation
compiler.hooks.thisCompilation.tap('CopyWebpackPlugin', (compilation) => {
// 添加资源的hooks
compilation.hooks.additionalAssets.tapAsync('CopyWebpackPlugin', async (cb) => {
// 将from中的资源复制到to中,输出出去
const { from, ignore } = this.options;
const to = this.options.to ? this.options.to : '.';
// context就是webpack配置
// 运行指令的目录
const context = compiler.options.context; // process.cwd()
// 将输入路径变成绝对路径
const absoluteFrom = path.isAbsolute(from) ? from : path.resolve(context, from);
// 1. 过滤掉ignore的文件
// globby(要处理的文件夹,options)
const paths = await globby(absoluteFrom, { ignore });
console.log(paths); // 所有要加载的文件路径数组
// 2. 读取paths中所有资源
const files = await Promise.all(
paths.map(async (absolutePath) => {
// 读取文件
const data = await readFile(absolutePath);
// basename得到最后的文件名称
const relativePath = path.basename(absolutePath);
// 和to属性结合
// 没有to --> reset.css
// 有to --> css/reset.css
const filename = path.join(to, relativePath);
return {
// 文件数据
data,
// 文件名称
filename
}
})
)
// 3. 生成webpack格式的资源
const assets = files.map((file) => {
const source = new RawSource(file.data);
return {
source,
filename: file.filename
}
})
// 4. 添加compilation中,输出出去
assets.forEach((asset) => {
compilation.emitAsset(asset.filename, asset.source);
})
cb();
})
})
}
}
module.exports = CopyWebpackPlugin;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具