原文:how to write a pluginjavascript
譯者:neal1991java
welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact mewebpack
LICENSE: MITgit
插件可以將webpack引擎的所有潛力暴露給第三方的開發者。經過使用階段構建回調,開發者可以將他們本身的行爲引入到webpack的構建過程當中。構建插件比構建loader更高級,由於你須要理解一些webpack低層次的內部鉤子。準備好閱讀一些源代碼吧!github
在開發插件的時候最重要的兩個資源就是compiler
和compilation
對象。理解它們的角色是拓展webpack引擎重要的第一步。web
compiler
對象表明了完整的配置的webpack環境。一旦開啓webpack以後,這個對象就被構建了,而且這個對象會使用全部操做設置,包括options, loaders, 以及plugins來進行配置。當將一個插件應用到webpack環境中,這個插件將會得到一個對於這個compiler的引用。使用這個compiler能夠訪問主要的webpack環境。數組
一個compilation
對象表明版本資源的一次構建。當運行webpack開發中間件的時候,每次檢測到文件變化的時候都會產生一個新的compilation,所以會生成一系列編譯後的資源。Compilation表示有關模塊資源,已編譯資源,已更改文件和監視依賴關係的當前狀態的信息。該compilation還提供了許多回調點,插件能夠選擇執行自定義操做。架構
這兩個組件是任何webpack插件(特別是compilation
)的內部一部分,所以開發者熟悉這些源代碼文件以後將會受益非凡:app
插件是實例對象,而且在它們的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}) ] };
使用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插件步驟是異步的,而且當你的插件完成運行的時候,傳遞一個必須被調用的回調函數。
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的一些特性在開發插件的時候變得頗有用。
在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
數組,所以您的插件必須將本身觀察的依賴項推送到每一個編譯中,以使它們保持監視。
與觀察圖相似,經過跟蹤它們的哈希值,能夠在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;