原文:how to write a plugin译者:neal1991
welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me
LICENSE: MIT
插件能够将webpack引擎的全部潜力暴露给第三方的开发者。通过使用阶段构建回调,开发者能够将他们自己的行为引入到webpack的构建过程中。构建插件比构建loader更高级,因为你需要理解一些webpack低层次的内部钩子。准备好阅读一些源代码吧!
Compiler以及Compilation
在开发插件的时候最重要的两个资源就是compiler
和compilation
对象。理解它们的角色是拓展webpack引擎重要的第一步。
-
compiler
对象代表了完整的配置的webpack环境。一旦开启webpack之后,这个对象就被构建了,并且这个对象会使用所有操作设置,包括options, loaders, 以及plugins来进行配置。当将一个插件应用到webpack环境中,这个插件将会获得一个对于这个compiler的引用。使用这个compiler可以访问主要的webpack环境。 - 一个
compilation
对象代表版本资源的一次构建。当运行webpack开发中间件的时候,每次检测到文件变化的时候都会产生一个新的compilation,因此会生成一系列编译后的资源。Compilation表示有关模块资源,已编译资源,已更改文件和监视依赖关系的当前状态的信息。该compilation还提供了许多回调点,插件可以选择执行自定义操作。
这两个组件是任何webpack插件(特别是compilation
)的内部一部分,因此开发者熟悉这些源代码文件之后将会受益非凡:
基本的插件架构
插件是实例对象,并且在它们的prototype上,会有一个apply
方法。当安装这个插件的时候,这个apply
方法就会被webpack compiler调用。这个apply
会给出一个对于潜在的webpack compiler的引用,保证了对于compiler回调的访问。一个简单的插件结构如下:
function HelloWorldPlugin(options) {
// Setup the plugin instance with options...
}
HelloWorldPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function() {
console.log('Hello World!');
});
};
module.exports = HelloWorldPlugin;
接着是安装这个插件,只要在你的webpack 配置plugins
数组里面添加一个实例:
var HelloWorldPlugin = require('hello-world');
var webpackConfig = {
// ... config settings here ...
plugins: [
new HelloWorldPlugin({options: true})
]
};
访问compilation
使用compiler对象,你可能绑定提供那个对于每一个新的compilation引用的回调。这些compilation提供对于在构建过程中对于很多步骤钩子的回调。
function HelloCompilationPlugin(options) {}
HelloCompilationPlugin.prototype.apply = function(compiler) {
// Setup callback for accessing a compilation:
compiler.plugin("compilation", function(compilation) {
// Now setup callbacks for accessing compilation steps:
compilation.plugin("optimize", function() {
console.log("Assets are being optimized.");
});
});
};
module.exports = HelloCompilationPlugin;
对于更多关于compiler
以及compilation
上的回调以及其他重要的对象,请参考 [[plugins API|plugins]] 文档。
异步compilation plugins
有一些compilation插件步骤是异步的,并且当你的插件完成运行的时候,传递一个必须被调用的回调函数。
function HelloAsyncPlugin(options) {}
HelloAsyncPlugin.prototype.apply = function(compiler) {
compiler.plugin("emit", function(compilation, callback) {
// Do something async...
setTimeout(function() {
console.log("Done with async work...");
callback();
}, 1000);
});
};
module.exports = HelloAsyncPlugin;
一个简单的例子
一旦我们可以锁定到webpack compiler以及每一个独立的compilation,我们可以利用引擎本身就能发挥无穷的潜力。我们能够重新格式化存在的文件,创建衍生文件,或者制造全新的资源。
让我们写一个简单的能够生成一个新的打包文件filelist.md
的插件例子;这个文件的内容会列出所有存在我们build之内的资源文件。这个插件可能看起来是这个样子的:
function FileListPlugin(options) {}
FileListPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
// Create a header string for the generated file:
var filelist = 'In this build:\n\n';
// Loop through all compiled assets,
// adding a new line item for each filename.
for (var filename in compilation.assets) {
filelist += ('- '+ filename +'\n');
}
// Insert this list into the Webpack build as a new file asset:
compilation.assets['filelist.md'] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
};
callback();
});
};
module.exports = FileListPlugin;
有用的插件模式
插件允许在webpack构建系统内发挥无尽可能的定制化。这允许你创建自定义的资源类型,执行特殊的构建调整,或者设置在使用中间件的时候进一步提升webpack运行时间。下面的webpack的一些特性在开发插件的时候变得很有用。
探索assets, chunks, modules, 以及dependencies
在compilation完成之后,compilation中的所有的结构都可能被遍历。
function MyPlugin() {}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
// Explore each chunk (build output):
compilation.chunks.forEach(function(chunk) {
// Explore each module within the chunk (built inputs):
chunk.modules.forEach(function(module) {
// Explore each source file path that was included into the module:
module.fileDependencies.forEach(function(filepath) {
// we've learned a lot about the source structure now...
});
});
// Explore each asset filename generated by the chunk:
chunk.files.forEach(function(filename) {
// Get the asset source for each file generated by the chunk:
var source = compilation.assets[filename].source();
});
});
callback();
});
};
module.exports = MyPlugin;
-
compilation.modules
: 在compilation中由模块(构建输入)组成的数组。每个模块管理来自于源代码库中的源文件的构建。 -
module.fileDependencies
: 包含在模块中的源文件路径数组。 这包括源JavaScript文件本身(例如:index.js
)以及所需的所有依赖项资源文件(样式表,图像等)。 查看依赖关系对于查看哪些源文件属于模块很有用。 -
compilation.chunks
: Compilation中由chunks组成的数组(构建输出)。 每个chunk管理最终渲染资源的组合。 -
chunk.modules
: 包含在一个chunk中的模块数组。 通过扩展,您可以查看每个模块的依赖关系,以查看传递到chunk中的原始源文件 -
chunk.files
: 由chunk生成的输出文件名的数组。 您可以从compilation.assets
表访问这些资源。
检测观察图
在运行webpack中间件时,每个compilation都包含一个fileDependencies
数组(正在监视的文件)和一个将观察文件路径映射到时间戳的fileTimestamps
哈希。 这些对于检测compilation中哪些文件已更改非常有用:
function MyPlugin() {
this.startTime = Date.now();
this.prevTimestamps = {};
}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
var changedFiles = Object.keys(compilation.fileTimestamps).filter(function(watchfile) {
return (this.prevTimestamps[watchfile] || this.startTime) < (compilation.fileTimestamps[watchfile] || Infinity);
}.bind(this));
this.prevTimestamps = compilation.fileTimestamps;
callback();
}.bind(this));
};
module.exports = MyPlugin;
您还可以将新的文件路径传入观察图,以便在这些文件更改时接收compilation触发器。 只需将有效的文件路径推送到compilation.fileDependencies
数组中即可将其添加到观察列表中。 注意:在每个compilation中重建fileDependencies
数组,因此您的插件必须将自己观察的依赖项推送到每个编译中,以使它们保持监视。
改变的chunks
与观察图类似,通过跟踪它们的哈希值,可以在compilation中监视更改的块(或模块)。
function MyPlugin() {
this.chunkVersions = {};
}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
var changedChunks = compilation.chunks.filter(function(chunk) {
var oldVersion = this.chunkVersions[chunk.name];
this.chunkVersions[chunk.name] = chunk.hash;
return chunk.hash !== oldVersion;
}.bind(this));
callback();
}.bind(this));
};
module.exports = MyPlugin;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。