如何編寫一個 Webpack Plugin

2021-02-21 前端精神時光屋
前言

上次寫了 如何編寫一個 Webpack Loader,今天來說說如何編寫一個 Webpack Plugin。

webpack 內部執行流程

一次完整的 webpack 打包大致是這樣的過程:

將命令行參數與 webpack 配置文件 合併、解析得到參數對象。參數對象傳給 webpack 執行得到 Compiler 對象。執行 Compiler 的 run 方法開始編譯。每次執行 run 編譯都會生成一個 Compilation 對象。觸發 Compiler 的 make 方法分析入口文件,調用 compilation 的 buildModule 方法創建主模塊對象。生成入口文件 AST(抽象語法樹),通過 AST 分析和遞歸加載依賴模塊。所有模塊分析完成後,執行 compilation 的 seal 方法對每個 chunk 進行整理、優化、封裝。最後執行 Compiler 的 emitAssets 方法把生成的文件輸出到 output 的目錄中。Plugin 作用

按我的理解,Webpack 插件的作用就是在 webpack 運行到某個時刻的時候,幫我們做一些事情。

在 Webpack 運行的生命周期中會廣播出許多事件,Plugin 可以監聽這些事件,在合適的時機通過 Webpack 提供的 API 改變輸出結果。

官方解釋是:

插件向第三方開發者提供了 webpack 引擎中完整的能力。使用階段式的構建回調,開發者可以引入它們自己的行為到 webpack 構建流程中。

❞編寫 Plugin

webpack 插件的組成:

一個 JS 命名函數或一個類(可以想下我們平時使用插件就是 new XXXPlugin()的方式)在插件類/函數的 (prototype) 上定義一個 apply 方法。通過 apply 函數中傳入 compiler 並插入指定的事件鉤子,在鉤子回調中取到 compilation 對象通過 compilation 處理 webpack 內部特定的實例數據如果是插件是異步的,在插件的邏輯編寫完後調用 webpack 提供的 callback

比如我們寫一個插件,生成一個版權的文件。

基本雛形
function CopyrightWebpackPlugin() {}

CopyrightWebpackPlugin.prototype.apply = function (compiler) {}

module.exports = CopyrightWebpackPlugin

也可以寫成類的形式:

class CopyrightWebpackPlugin {
  apply(compiler) {
    console.log(compiler)
  }
}

module.exports = CopyrightWebpackPlugin

webpack 在啟動之後,在讀取配置的過程中會先執行new CopyrightWebpackPlugin(options)操作,初始化一個CopyrightWebpackPlugin實例對象。在初始化 compiler 對象之後,會調用上述實例對象的apply方法並將compiler對象傳入。

在apply方法中,通過compiler對象來監聽 webpack 生命周期中廣播出來的事件,我們也可以通過 compiler 對象來操作 webpack 的輸出。

Compiler 和 Compilation

在插件開發中最重要的兩個對象是 compiler 和 compilation 對象。

compiler 對象代表了完整的 webpack 環境配置,在初始化 compiler 對象之後,通過調用插件實例的 apply 方法,作為其參數傳入。這個對象在啟動 webpack 時被一次性建立,並包含了 webpack 環境的所有的配置信息,包括 options,loader 和 plugin。當在 webpack 環境中應用一個插件時,插件將收到此 compiler 對象的引用。可以使用它來訪問 webpack 的主環境。

❞❝

compilation 對象會作為 plugin 內置事件回調函數的參數,一個 compilation 對象包含了當前的模塊資源、編譯生成資源、變化的文件以及被跟蹤依賴的狀態信息。當 Webpack 以開發模式運行時,每當檢測到一個文件變化,一次新的 compilation 將被創建。compilation 對象也提供了很多事件回調供插件做擴展。通過 compilation 也能讀取到 compiler 對象。

❞編碼

下面代碼為生成一個版權 txt 文件,新建文件src/plugins/copyright-webpack-plugin.js:

class CopyrightWebpackPlugin {
  apply(compiler) {
    // emit 鉤子是生成資源到 output 目錄之前執行,emit 是一個異步串行鉤子,需要用 tapAsync 來註冊
    compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, callback) => {
      // 回調方式註冊異步鉤子
      const copyrightText = '版權歸 JackySummer 所有'
      // compilation存放了這次打包的所有內容
      // 所有待生成的文件都在它的 assets 屬性上
      compilation.assets['copyright.txt'] = {
        // 添加copyright.txt
        source: function () {
          return copyrightText
        },
        size: function () {
          // 文件大小
          return copyrightText.length
        },
      }
      callback() // 必須調用
    })
  }
}

module.exports = CopyrightWebpackPlugin

webpack 中許多對象擴展自 Tapable 類。這個類暴露 tap, tapAsync 和 tapPromise 方法,可以使用這些方法,注入自定義的構建步驟,這些步驟將在整個編譯過程中不同時機觸發。

使用 tapAsync 方法來訪問插件時,需要調用作為最後一個參數提供的回調函數。

在 webpack.config.js

const path = require('path')
const CopyrightWebpackPlugin = require('./src/plugins/copyright-webpack-plugin')

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  plugins: [new CopyrightWebpackPlugin()],
}

執行 webpack 命令,就會看到 dist 目錄下生成copyright.txt文件

如果在配置文件使用 plugin 時傳入參數該怎麼獲得呢,可以在插件類添加構造函數拿到:

 plugins: [
  new CopyrightWebpackPlugin({
    name: 'jacky',
  }),
],

在copyright-webpack-plugin.js中

class CopyrightWebpackPlugin {
  constructor(options = {}) {
    console.log('options', options) // options { name: 'jacky' }
  }
}

參考文章:揭秘 webpack plugin


歡迎關注我掘金帳號和Github技術博客:

掘金:https://juejin.im/user/1257497033714477Github:https://github.com/Jacky-Summer覺得對你有幫助或有啟發的話歡迎 star,你的鼓勵是我持續創作的動力~如需在微信公眾號平臺轉載請聯繫作者授權同意,其它途徑轉載請在文章開頭註明作者和文章出處。❞

相關焦點

  • 圖解Webpack——實現Plugin
    Plugin是webpack生態系統的重要組成部分,其目的是解決loader無法實現的其他事,可用於執行範圍更廣的任務,為webpack帶來很大的靈活性。目前存在的plugin並不能完全滿足所有的開發需求,所以定製化符合自己需求的plugin成為學習webpack的必經之路。下面將逐步闡述plugin開發中幾個關鍵技術點並實現plugin。
  • webpack教程:如何從頭開始設置 webpack 5
    什麼是 webpack?現在,大多數網站不再只是單單的由原生JS+純HTML編寫的,還涉及一些瀏覽器無法理解的語言,如果項目大,文件多,對應的體積就大。所以要壓縮文件和翻譯成所有瀏覽器都能理解的東西,這就是webpack的用武之地。
  • 擼一個webpack插件!!
    而將這些插件控制在webapck事件流上的運行的就是webpack自己寫的基礎類Tapable。Tapable暴露出掛載plugin的方法,使我們能將插件控制在webapack事件流上運行(如下圖)。後面我們將看到核心的對象 Compiler,Compilation等都是繼承於Tabable類。Tabable是什麼?
  • Webpack 3 從入門到放棄
    再次,為了清晰有效地講解,我會演示從零編寫 demo,只要一步步跟著做,就會清晰許多。最後,官方文檔也是個坑爹貨!Webpack,何許人也?借用官方的說法:webpack is a module bundler.
  • Webpack的使用指南-Webpack的常用解決方案
    說是解決方案實際上更像是webpack的插件索引。寫這一篇的目的是為了形成一個所以,將來要用時直接來查找即可。1.自動構建HTML,可壓縮空格,可給引用的js加版本號或隨機數:html-webpack-plugin解決方案:使用插件 html-webpack-pluginwebpack.config.js如下:module.exports = {entry: '.
  • 前端必備技能 webpack - 4. webpack處理CSS資源
    webpack 配置文件,對創建項目過程有疑問的同學,可以查看 前端必備技能 webpack - 2. webpack環境安裝 的第四部分。這個時候,我們需要藉助一個插件將 css 文件單獨提取出來:npm i mini-css-extract-plugin -D  安裝完畢後我們修改一下項目的目錄,並創建一些文件:
  • 18款Webpack插件,總會有你想要的!
    Plugin是一個擴展器,它豐富了webpack本身,針對的是loader結束後,webpack打包的整個過程,它並不直接操作文件,而是基於事件機制工作,會監聽webpack打包過程中的某些分段,執行廣泛的任務。
  • 你必須知道的 webpack 插件原理分析
    在 webpack 中,專注於處理 webpack 在編譯過程中的某個特定的任務的功能模塊,可以稱為插件。它和 loader 有以下區別:loader 是一個轉換器,將 A 文件進行編譯成 B 文件,比如:將 A.less 轉換為 A.css,單純的文件轉換過程。
  • 18個常用 webpack插件,總會有適合你的!
    專注處理 webpack 在編譯過程中的某個特定的任務的功能模塊,可以稱為插件。Plugin 是一個擴展器,它豐富了 webpack 本身,針對是 loader 結束後,webpack 打包的整個過程,它並不直接操作文件,而是基於事件機制工作,會監聽 webpack 打包過程中的某些節點,執行廣泛的任務。是一個獨立的模塊。模塊對外暴露一個 js 函數。
  • 前端工程化(ES6模塊化和webpack打包css,less,scss,圖片,字體...
    */webpack是一個流行的前端項目構建工具,可以解決目前web開發的困境。項目中配置webpack/* 1).打開項目目錄終端,輸入命令: npm install webpack webpack-cli -D 2).然後在項目根目錄中,創建一個 webpack.config.js 的配置文件用來配置webpack 在 webpack.config.js 文件中編寫代碼進行webpack
  • 重學webpack4之基礎篇
    >mini-css-extract-plugin和optimize-css-assets-webpack-plugin提取css,建議使用contenthashmodule: { rules: [ { test: /.s?
  • webpack如何編寫一個自己的loader
    Loader 只是一個導出為函數的 JavaScript 模塊。現在我們可以知道其實loader就是一個函數,我們可以在這個函數裡做一些事情。誰會調用這個函數呢(loader runner 會調用這個函數,然後把上一個 loader 產生的結果或者資源文件(resource file)傳入進去。)Loader職責是什麼?
  • webpack系列---loader
    webpack本身只能打包Javascript文件,對於其他資源例如 css,圖片,或者其他的語法集比如jsx,是沒有辦法加載的。 這就需要對應的loader將資源轉化,加載進來。所謂 loader 只是一個導出為函數的 JavaScript 模塊。
  • Webpack打包全世界
    核心和概念本質上,webpack 是一個現代 JavaScript 應用程式的靜態模塊打包器(module bundler)。當 webpack 處理應用程式時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程式需要的每個模塊,然後將所有這些模塊打包成一個或多個 bundle。
  • Webpack 的 Bundle Split 和 Code Split 區別和應用
    vendors 中去,那麼我們繼續配置 webpack.config.js:將每個 npm 包單獨分離出來這裡我們需要使用到 webpack.HashedModuleIdsPlugin 這個插件參考官方文檔(https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks)
  • webpack4 處理頁面
    的插件集合,可以用一些插件擴展webpack4 的功能,就比如HtmlWebpackPlugin2.HtmlWebpackPlugin的主要作用是:為html文件中引入的外部資源如script、link動態添加每次compile後的hash,防止引用緩存的外部文件問題可以生成創建html入口文件,比如單頁面可以生成一個html文件入口,配置N個html-webpack-plugin可以生成N個頁面入口
  • Webpack 多入口配置,看這篇,足夠了
    準備工作首先我們 vue init webpack multi-entry-vue 使用 vue-cli 創建一個 webpack 模版的項。文件結構如下:.本文由於是入口和出口相關的配置,所以內容主要圍繞著 entry 、 output 和一個重要的 webpack 插件 html-webpack-plugin,這個插件是跟打包出來的 HTML 文件密切相關,主要有下面幾個作用:根據模版生成 HTML 文件;給生成的 HTML 文件引入外部資源比如 link、 script 等;改變每次引入的外部文件的
  • 【webpack】webpack 中最易混淆的 5 個知識點
    其實 webpack 懶加載是用內置的一個插件 SplitChunksPlugin[6] 實現的,這個插件裡面有些默認配置項[7],比如說 automaticNameDelimiter,默認的分割符就是 ~,所以最後的文件名才會出現這個符號,這塊兒內容我就不引申了,感興趣的同學可以自己研究一下。
  • webpack基本配置有哪些?如何搭建webpack?
    主要內容webpack學習目標第一節 webpack介紹1.webpack介紹webpack 是一個模塊打包器。webpack 的主要目標是將 JavaScript 文件打包在一起,打包後的文件用於在瀏覽器中使用,但它也能夠勝任轉換(transform)、打包(bundle)。
  • 記 webpack 中文文檔的一次優化
    如此修改又遇到了難題:webpack 本身的文檔構建不能識別 {#} 這種形式即使能夠識別,webpack 文檔大約有 200 篇左右的文檔,要手動修改?我們開始針對文檔站點的部署以及構建進行查看,然後思考如何對解決這兩個問題。