如今項目大部分都使用webpack 進行編譯,一方面 webpack 生命力比較旺盛,另外一方面 webpack 的生態很是完善。咱們90%的場景都可以知足。但有時候也會遇到一些特定的業務場景,好比有時候會遇到須要對編譯後的文件進行一些文件注入 等其餘特定化的需求。怎麼辦呢?那就來寫一個業務定製化的webpack 插件吧。webpack
官方文檔可見: https://webpack.js.org/contribute/writing-a-plugin/#creating-a-pluginweb
首先咱們瞭解下一個插件的主要結構api
class DtoolPlugin { // 構造函數,進行參數校驗等功能 constructor(args) {} // 定義插件執行的方法體,主要邏輯都在這裏完成,webpack 會自動調用此方法 apply(compiler) {} }
下面咱們來看下 compiler 這個關鍵對象,compile 提供了 webpack 一系列操做的鉤子函數,下面說一說經常使用的hooksapp
官方文檔可見:https://webpack.js.org/api/compiler-hooks/#watching異步
下面以真實案例來說解async
apply(compiler) { const metaParams = this.metaParams; const tester = {test: this.test}; compiler.hooks.compilation.tap('DtoolPlugin', (compilation) => { compilation.hooks.optimizeChunkAssets.tapAsync('DtoolPlugin', (chunks, done) => { wrapChunks(compilation, chunks); done(); }) }); // 注入文件方法 function wrapChunks(compilation, chunks) { chunks.forEach(chunk => { const args = { hash: compilation.hash, chunkhash: chunk.hash }; chunk.files.forEach(fileName => { if (ModuleFilenameHelpers.matchObject(tester, fileName)) { const content = 'xxxxx'; // 注入內容 // assets 對象是編譯後的對象,以文件名爲key , 值爲文件內容 、 文件size 等一系列信息, 經過asset 能夠對文件名字 , 文件內容作變動 compilation.assets[fileName] = new ConcatSource( compilation.assets[fileName], String(content), ); } }); }); } }
hooks 調用的時候有個類型 plugin types 支持 tap , 有些支持異步類型 tapAsync tapPromise函數
compiler.hooks.run.tapAsync('MyPlugin', (source, target, routesList, callback) => { console.log('Asynchronously tapping the run hook.'); callback(); }); compiler.hooks.run.tapPromise('MyPlugin', (source, target, routesList) => { return new Promise(resolve => setTimeout(resolve, 1000)).then(() => { console.log('Asynchronously tapping the run hook with a delay.'); }); }); compiler.hooks.run.tapPromise('MyPlugin', async (source, target, routesList) => { await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Asynchronously tapping the run hook with a delay.'); });