插件模式
title: 插件模式
sort: 5
contributors:
- nveenjain
- EugeneHlushko
- benglynn
- raj-sapalya
插件模式
插件为在 webpack 构建系统中执行自定义提供了无限的机会。这允许你创建自定义资源类型、执行独特的构建修改,甚至在使用中间件时增强 webpack 运行时。以下是 webpack 在编写插件时一些有用的特性。
探索资源、chunk、模块和依赖
编译被封存(sealed)后,编译中的所有结构都可以被遍历。
js
class MyPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync("MyPlugin", (compilation, callback) => {
// Explore each chunk (build output):
for (const chunk of compilation.chunks) {
// Explore each module within the chunk (built inputs):
for (const module of chunk.getModules()) {
// Explore each source file path that was included into the module:
if (module.buildInfo && module.buildInfo.fileDependencies) {
for (const fileDependency of module.buildInfo.fileDependencies) {
// we've learned a lot about the source structure now...
console.log(fileDependency);
}
}
}
// Explore each asset filename generated by the chunk:
for (const filename of chunk.files) {
// Get the asset source for each file generated by the chunk:
const source = compilation.assets[filename].source();
}
}
callback();
});
}
}
export default MyPlugin;
compilation.modules:编译中的模块(构建输入)集合。每个模块管理来自你源代码库的原始文件的构建。
弃用警告:数组函数仍然可以正常工作。
module.fileDependencies:包含在模块中的源文件路径数组。这包括源 JavaScript 文件本身(例如:index.js),以及它所需的所有依赖资源文件(样式表、图片等)。审查依赖关系有助于查看哪些源文件属于一个模块。compilation.chunks:编译中的 chunk(构建输出)集合。每个 chunk 管理最终渲染资源的组成。
弃用警告:数组函数仍然可以正常工作。
chunk.getModules():包含在 chunk 中的模块数组。以此类推,你可以查看每个模块的依赖关系,以了解哪些原始源文件输入到一个 chunk。chunk.files:由 chunk 生成的输出文件名的Set。你可以从compilation.assets表中访问这些资源的源码。
监听监视图(watch graph)
在运行 webpack 中间件时,每次编译都包含一个 fileDependencies Set(哪些文件正在被监听)和一个 fileTimestamps Map,用于将监听的文件路径映射到时间戳。这些对于检测编译中哪些文件发生了变化非常有用:
js
class MyPlugin {
constructor() {
this.startTime = Date.now();
this.prevTimestamps = new Map();
}
apply(compiler) {
compiler.hooks.emit.tapAsync("MyPlugin", (compilation, callback) => {
const changedFiles = [...compilation.fileTimestamps.keys()].filter(
(watchfile) =>
(this.prevTimestamps.get(watchfile) || this.startTime) <
(compilation.fileTimestamps.get(watchfile) || Infinity),
);
this.prevTimestamps = compilation.fileTimestamps;
callback();
});
}
}
export default MyPlugin;
你还可以向监视图中添加新的文件路径,以便在这些文件发生变化时收到编译触发。将有效的文件路径添加到 compilation.fileDependencies Set 中,即可将它们加入监听文件。
提示:
fileDependenciesSet在每次编译中都会重建,因此你的插件必须将自己的监听依赖添加到每次编译中,才能保持它们处于监听状态。
警告:自 webpack 5 起,
compilation.fileDependencies、compilation.contextDependencies和compilation.missingDependencies现在是Set,而不是Sortable Set,因此不再排序。
已更改的 chunk
与监视图类似,你可以通过跟踪 chunk 的内容哈希来监控编译中已更改的 chunk。
警告:
compilation.chunks是一个Set,而不是数组。直接对其调用.filter()或.map()会抛出TypeError。
请使用for...of或先将其展开为数组。
警告:
chunk.hash未定义在 chunk 对象上。请改用chunk.contentHash.javascript。
访问chunk.hash会返回undefined,导致每次重新构建时每个 chunk 都显示为已更改。
js
class MyPlugin {
constructor() {
this.chunkVersions = {};
}
apply(compiler) {
compiler.hooks.emit.tapAsync("MyPlugin", (compilation, callback) => {
const changedChunks = [];
for (const chunk of compilation.chunks) {
const key = chunk.id ?? chunk.name;
const currentHash = chunk.contentHash.javascript;
if (currentHash === undefined) continue;
const oldHash = this.chunkVersions[key];
this.chunkVersions[key] = currentHash;
if (currentHash !== oldHash) {
changedChunks.push(chunk);
}
}
if (changedChunks.length > 0) {
console.log(
"Changed chunks:",
changedChunks.map((chunk) => chunk.id),
);
}
callback();
});
}
}
export default MyPlugin;
提示:使用
chunk.id ?? chunk.name作为跟踪键。某些 chunk 有id但没有name,因此仅依赖chunk.name可能会遗漏变更。
CSS 或其他非 JS chunk 的contentHash可能没有javascript键——continue守卫可以安全地跳过它们。
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
