Webpack 插件開發如此簡單

【Webpack】Webpack 插件開發如此簡單!

本文使用的Webpack-Quickly-Starter快速搭建 Webpack4 本地學習環境。 建議多閱讀 Webpack 文檔《Writing a Plugin》章節,學習開發簡單插件。

本文將帶你一起開發你的第一個 Webpack 插件,從 Webpack 配置工程師,邁向 Webpack 開發工程師!做自己的輪子,讓別人用去吧。

完整代碼存放在:https://github.com/pingan8787/script-timestamp-webpack-plugin

Webpack 插件開發如此簡單

一、背景介紹

本文靈感源自業務中的經驗總結,不怕神一樣的產品,只怕一根筋的開發

在項目打包遇到問題:“當項目託管到 CDN 平臺,希望實現項目中的 index.js 不被緩存”。因為我們需要修改 index.js 中的內容,不想用戶被緩存。

思考一陣,有這麼幾種思路:

  1. 在 CDN 平臺中過濾該文件的緩存設置;
  2. 查找 DOM 元素,修改該>
  3. 打包時動態創建>

(聰明的你還有其他方法,歡迎討論)

思路分析:

  1. 顯然修改 CDN 設置的話,治標不治本;
  2. 在模版文件中,添加>
  3. 需要在 index.html 生成之前,修改 js 文件的路徑,並添加時間戳。

於是我準備使用第三種方式,在 index.html 生成之前完成下面修改:

Webpack 插件開發如此簡單

問題簡單,實際還是想試試開發 Webpack Plugin。

二、基礎知識

Webpack 使用階段式的構建回調,開發者可以引入它們自己的行為到 Webpack 構建流程中。在開發之前,需要了解以下 Webpack 相關概念:

2.1 Webpack 插件組成

在自定義插件之前,我們需要了解,一個 Webpack 插件由哪些構成,下面摘抄文檔:

  • 一個具名 JavaScript 函數;
  • 在它的原型上定義 apply 方法;
  • 指定一個觸及到 Webpack 本身的事件鉤子;
  • 操作 Webpack 內部的實例特定數據;
  • 在實現功能後調用 Webpack 提供的 callback。

2.2 Webpack 插件基本架構

插件由一個構造函數實例化出來。構造函數定義 apply 方法,在安裝插件時,apply 方法會被 Webpack compiler 調用一次。apply 方法可以接收一個 Webpack compiler對象的引用,從而可以在回調函數中訪問到 compiler 對象。

官方文檔提供一個簡單的插件結構:

<code>class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap('Hello World Plugin', (
stats /* 在 hook 被觸及時,會將 stats 作為參數傳入。 */
) => {
console.log('Hello World!');
});
}
}
module.exports = HelloWorldPlugin;
/<code>

使用插件:

<code>// webpack.config.js
var HelloWorldPlugin = require('hello-world');

module.exports = {
// ... 這裡是其他配置 ...
plugins: [new HelloWorldPlugin({ options: true })]
};
/<code>

2.3 HtmlWebpackPlugin 介紹

HtmlWebpackPlugin 簡化了 HTML 文件的創建,以便為你的 Webpack 包提供服務。這對於在文件名中包含每次會隨著編譯而發生變化哈希的 webpack bundle 尤其有用。

插件的基本作用概括:生成 HTML 文件

html-webapck-plugin 插件

兩個主要作用:

  • 為 HTML 文件引入外部資源(如>
  • 動態創建 HTML 入口文件,如單頁應用的 index.html文件。

html-webapck-plugin 插件原理介紹:

  • 讀取 Webpack 中 entry 配置的相關入口 chunk 和 extract-text-webpack-plugin 插件抽取的 CSS 樣式;
  • 將樣式插入到插件提供的 template 或 templateContent 配置指定的模版文件中;
  • 插入方式是:通過 link 標籤引入樣式,通過>

三、開發流程

本文開發的 自動添加時間戳引用腳本文件(SetScriptTimestampPlugin) 插件實現的原理:通過 HtmlWebpackPlugin 生成 HTML 文件前,將模版文件預留位置替換成腳本,腳本中執行自動添加時間戳來引用腳本文件。

3.1 插件運行機制

Webpack 插件開發如此簡單

SetScriptTimestampPlugin 運行機制.png

3.2 初始化插件文件

新建 SetScriptTimestampPlugin.js 文件,並參考官方文檔中插件的基本結構,初始化插件代碼:

<code>// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
apply(compiler) {
compiler.hooks.done.tap('SetScriptTimestampPlugin',
(compilation, callback) => {
console.log('SetScriptTimestampPlugin!');
});
}
}
module.exports = SetScriptTimestampPlugin;
/<code>

apply 方法為插件原型方法,接收 compiler 作為參數。

3.3 選擇插件觸發時機

選擇插件觸發時機,其實是選擇插件觸發的 compiler 鉤子(即何時觸發插件)。Webpack 提供鉤子有很多,這裡簡單介紹幾個,完整具體可參考文檔《Compiler Hooks》:

  • entryOption : 在 webpack 選項中的 entry 配置項 處理過之後,執行插件。
  • afterPlugins : 設置完初始插件之後,執行插件。
  • compilation : 編譯創建之後,生成文件之前,執行插件。。
  • emit : 生成資源到 output 目錄之前。
  • done : 編譯完成。

我們插件應該是要在 HTML 輸出之前,動態添加>

<code>// SetScriptTimestampPlugin.js

class SetScriptTimestampPlugin {
apply(compiler) {
- compiler.hooks.done.tap('SetScriptTimestampPlugin',
+ compiler.hooks.compilation.tap('SetScriptTimestampPlugin',
(compilation, callback) => {
console.log('SetScriptTimestampPlugin!');
});
}
}
module.exports = SetScriptTimestampPlugin;
/<code>

在 compiler.hooks 下指定事件鉤子函數,便會觸發鉤子時,執行回調函數。Webpack 提供三種觸發鉤子的方法:

  • tap :以同步方式觸發鉤子;
  • tapAsync :以異步方式觸發鉤子;
  • tapPromise :以異步方式觸發鉤子,返回 Promise;

這三種方式能選擇的鉤子方法也不同,由於 compilation 是 SyncHook 同步鉤子,所以採用 tap 觸發方式。tap 方法接收兩個參數:插件名稱和回調函數。

3.4 添加插件替換入口

我們原理上是將模版文件中,指定替換入口,再替換成需要執行的腳本。

Webpack 插件開發如此簡單

image.png

所以我們在模版文件 template.html 中添加


分享到:


相關文章: