webpack 提供了一個如何開發 webpack 插件的介紹,你能夠直接訪問這裏查看,這裏提供一個擴展 HtmlWebpackPlugin 的開發實例。javascript
前面咱們介紹過 HtmlWebpackPlugin, 這個插件容許將 webpack 動態打包的輸出注入到頁面中,可是,有的時候咱們須要在這個頁面中注入一些自定義的樣式表或者腳本,HtmlWebpackPlugin 並不支持這個特性。有人向插件做者提了建議,這裏是討論的內容,結果是插件提供了幾個事件來支持本身來實現這個特性。咱們經過一個實例來演示如何使用這些事件來擴展 webpack。html
咱們但願可以自動插入一個腳本的 script 在 webpack 生成的 script 以前,以便提早加載咱們自定義的數據。最後生成的 HTML 相似這樣的效果。java
<script type="text/javascript" src="./configuration/config.js"></script> <script type="text/javascript" src="style.bundle.js"></script> <script type="text/javascript" src="app.bundle.js"></script>
第一行是咱們指望注入的腳本,其它兩行是 webpack 導出的腳本。webpack
做爲一個 webpack 的插件,使用方式是這樣的。git
plugins: [ new MyPlugin({ paths: ["./configuration/config.js"] }), new HtmlwebpackPlugin({ title: 'Hello Angular2!', template: './src/index.html', inject: true }) ],
全部的插件定義在 plugins 中,插件組成的一個數組,每一個元素是一個插件的對象實例,具體傳遞什麼參數,是你本身定義的。github
從使用方式中能夠看出,其實咱們須要一個 JavsScript 的類函數,也就是說,寫 webpack 插件就是定義一個這樣的函數,這個函數須要接收參數。web
webpack 還要求這個對象提供一個名爲 apply 的函數,這個函數定義在插件的原型上,webpack 會調用插件實例的這個方法,在調用的時候還會傳遞一個參數,以便咱們訪問 webpack 的上下文信息。npm
官方提供的實例函數以下,最後一行是使用 CommonJs 風格導出這個插件。數組
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;
在咱們的需求中,咱們但願傳遞一個名爲 paths 的路徑參數,其中的每一個路徑須要生成一個 script 元素,插入到 webpack 導出的 script 以前。app
new MyPlugin({ paths: ["./configuration/config.js"] }),
在咱們的插件中,須要保存這個參數,以便在 apply 函數中使用。
function MyPlugin(options) { // Configure your plugin with options... this.options = options; }
直接保存到當前的對象實例中,在配合 new 的時候,this 就是剛剛建立的插件對象實例了。
在 webpack 調用插件對象的 apply 方式的時候,咱們首先應該獲取咱們保存的參數,使用 this 訪問當前對象,獲取剛剛保存的參數。
MyPlugin.prototype.apply = function(compiler) { // ... var paths = this.options.paths; };
在咱們的 apply 方法內,須要調用 compiler 的 plugin 函數。這個函數註冊到 webpack 各個處理階段上,能夠支持的參數有:
咱們這裏使用了 compilation 編譯任務。
MyPlugin.prototype.apply = function(compiler) { var paths = this.options.paths; compiler.plugin('compilation', function(compilation, options) {
}); };
webpack 會給咱們提供的回調函數提供參數,咱們能夠註冊編譯階段的事件了。html-webpack-plugin 提供了一系列事件。
Async:
html-webpack-plugin-before-html-generation
html-webpack-plugin-before-html-processing
html-webpack-plugin-alter-asset-tags
html-webpack-plugin-after-html-processing
html-webpack-plugin-after-emit
Sync:
html-webpack-plugin-alter-chunks
咱們能夠註冊到它處理 HTML 以前,使用 html-webpack-plugin-before-html-processing 事件。
MyPlugin.prototype.apply = function(compiler) { var paths = this.options.paths; compiler.plugin('compilation', function(compilation, options) { compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) {
......
}); }); };
在這個回調函數中,咱們能夠獲得 html-webpack-plugin 提供的上下文對象,好比,它準備生成 script 所對應的 javascript 文件路徑就保存在 htmlPluginData.assets.js 數組中,它會根據這個數組中的路徑,依次生成 script 元素,而後插入到 Html 網頁中。
咱們須要的就是就咱們的路徑插入到這個數組的前面。
MyPlugin.prototype.apply = function(compiler) { var paths = this.options.paths; compiler.plugin('compilation', function(compilation, options) { compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) { for (var i = paths.length - 1; i >= 0; i--) { htmlPluginData.assets.js.unshift(paths[i]); } callback(null, htmlPluginData); }); }); };
完整的插件代碼以下所示。
function MyPlugin(options) { this.options = options; } MyPlugin.prototype.apply = function(compiler) { var paths = this.options.paths; compiler.plugin('compilation', function(compilation, options) { compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) { for (var i = paths.length - 1; i >= 0; i--) { htmlPluginData.assets.js.unshift(paths[i]); } callback(null, htmlPluginData); }); }); }; module.exports = MyPlugin;
最後一行是導出咱們的插件。
經過 webpack 的插件機制,咱們能夠自由地擴展 webpack ,實現咱們須要的特性。