国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Webpack5入門到原理18:Plugin 原理

這篇具有很好參考價(jià)值的文章主要介紹了Webpack5入門到原理18:Plugin 原理。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

Plugin 的作用

通過插件我們可以擴(kuò)展 webpack,加入自定義的構(gòu)建行為,使 webpack 可以執(zhí)行更廣泛的任務(wù),擁有更強(qiáng)的構(gòu)建能力。

Plugin 工作原理

webpack 就像一條生產(chǎn)線,要經(jīng)過一系列處理流程后才能將源文件轉(zhuǎn)換成輸出結(jié)果。 這條生產(chǎn)線上的每個(gè)處理流程的職責(zé)都是單一的,多個(gè)流程之間有存在依賴關(guān)系,只有完成當(dāng)前處理后才能交給下一個(gè)流程去處理。
插件就像是一個(gè)插入到生產(chǎn)線中的一個(gè)功能,在特定的時(shí)機(jī)對(duì)生產(chǎn)線上的資源做處理。webpack 通過 Tapable 來組織這條復(fù)雜的生產(chǎn)線。 webpack 在運(yùn)行過程中會(huì)廣播事件,插件只需要監(jiān)聽它所關(guān)心的事件,就能加入到這條生產(chǎn)線中,去改變生產(chǎn)線的運(yùn)作。
webpack 的事件流機(jī)制保證了插件的有序性,使得整個(gè)系統(tǒng)擴(kuò)展性很好。
——「深入淺出 Webpack」

站在代碼邏輯的角度就是:webpack 在編譯代碼過程中,會(huì)觸發(fā)一系列 Tapable 鉤子事件,插件所做的,就是找到相應(yīng)的鉤子,往上面掛上自己的任務(wù),也就是注冊(cè)事件,這樣,當(dāng) webpack 構(gòu)建的時(shí)候,插件注冊(cè)的事件就會(huì)隨著鉤子的觸發(fā)而執(zhí)行了。

Webpack 內(nèi)部的鉤子

什么是鉤子

鉤子的本質(zhì)就是:事件。為了方便我們直接介入和控制編譯過程,webpack 把編譯過程中觸發(fā)的各類關(guān)鍵事件封裝成事件接口暴露了出來。這些接口被很形象地稱做:hooks(鉤子)。開發(fā)插件,離不開這些鉤子。

Tapable

Tapable 為 webpack 提供了統(tǒng)一的插件接口(鉤子)類型定義,它是 webpack 的核心功能庫(kù)。webpack 中目前有十種 hooks,在 Tapable 源碼中可以看到,他們是:

// https://github.com/webpack/tapable/blob/master/lib/index.js
exports.SyncHook = require("./SyncHook");
exports.SyncBailHook = require("./SyncBailHook");
exports.SyncWaterfallHook = require("./SyncWaterfallHook");
exports.SyncLoopHook = require("./SyncLoopHook");
exports.AsyncParallelHook = require("./AsyncParallelHook");
exports.AsyncParallelBailHook = require("./AsyncParallelBailHook");
exports.AsyncSeriesHook = require("./AsyncSeriesHook");
exports.AsyncSeriesBailHook = require("./AsyncSeriesBailHook");
exports.AsyncSeriesLoopHook = require("./AsyncSeriesLoopHook");
exports.AsyncSeriesWaterfallHook = require("./AsyncSeriesWaterfallHook");
exports.HookMap = require("./HookMap");
exports.MultiHook = require("./MultiHook");

Plugin 構(gòu)建對(duì)象

Compiler

compiler 對(duì)象中保存著完整的 Webpack 環(huán)境配置,每次啟動(dòng) webpack 構(gòu)建時(shí)它都是一個(gè)獨(dú)一無二,僅僅會(huì)創(chuàng)建一次的對(duì)象。

這個(gè)對(duì)象會(huì)在首次啟動(dòng) Webpack 時(shí)創(chuàng)建,我們可以通過 compiler 對(duì)象上訪問到 Webapck 的主環(huán)境配置,比如 loader 、 plugin 等等配置信息。

它有以下主要屬性:

  • compiler.options 可以訪問本次啟動(dòng) webpack 時(shí)候所有的配置文件,包括但不限于 loaders 、 entry 、 output 、 plugin 等等完整配置信息。
  • compiler.inputFileSystem 和 compiler.outputFileSystem 可以進(jìn)行文件操作,相當(dāng)于 Nodejs 中 fs。
  • compiler.hooks 可以注冊(cè) tapable 的不同種類 Hook,從而可以在 compiler 生命周期中植入不同的邏輯。

compiler hooks 文檔

Compilation

compilation 對(duì)象代表一次資源的構(gòu)建,compilation 實(shí)例能夠訪問所有的模塊和它們的依賴。

一個(gè) compilation 對(duì)象會(huì)對(duì)構(gòu)建依賴圖中所有模塊,進(jìn)行編譯。 在編譯階段,模塊會(huì)被加載(load)、封存(seal)、優(yōu)化(optimize)、 分塊(chunk)、哈希(hash)和重新創(chuàng)建(restore)。

它有以下主要屬性:

  • compilation.modules 可以訪問所有模塊,打包的每一個(gè)文件都是一個(gè)模塊。
  • compilation.chunks chunk 即是多個(gè) modules 組成而來的一個(gè)代碼塊。入口文件引入的資源組成一個(gè) chunk,通過代碼分割的模塊又是另外的 chunk。
  • compilation.assets 可以訪問本次打包生成所有文件的結(jié)果。
  • compilation.hooks 可以注冊(cè) tapable 的不同種類 Hook,用于在 compilation 編譯模塊階段進(jìn)行邏輯添加以及修改。

compilation hooks 文檔

開發(fā)一個(gè)插件

最簡(jiǎn)單的插件

plugins/test-plugin.js

class TestPlugin {
  constructor() {
    console.log("TestPlugin constructor()");
  }
  // 1. webpack讀取配置時(shí),new TestPlugin() ,會(huì)執(zhí)行插件 constructor 方法
  // 2. webpack創(chuàng)建 compiler 對(duì)象
  // 3. 遍歷所有插件,調(diào)用插件的 apply 方法
  apply(compiler) {
    console.log("TestPlugin apply()");
  }
}

module.exports = TestPlugin;

注冊(cè) hook

class TestPlugin {
  constructor() {
    console.log("TestPlugin constructor()");
  }
  // 1. webpack讀取配置時(shí),new TestPlugin() ,會(huì)執(zhí)行插件 constructor 方法
  // 2. webpack創(chuàng)建 compiler 對(duì)象
  // 3. 遍歷所有插件,調(diào)用插件的 apply 方法
  apply(compiler) {
    console.log("TestPlugin apply()");

    // 從文檔可知, compile hook 是 SyncHook, 也就是同步鉤子, 只能用tap注冊(cè)
    compiler.hooks.compile.tap("TestPlugin", (compilationParams) => {
      console.log("compiler.compile()");
    });

    // 從文檔可知, make 是 AsyncParallelHook, 也就是異步并行鉤子, 特點(diǎn)就是異步任務(wù)同時(shí)執(zhí)行
    // 可以使用 tap、tapAsync、tapPromise 注冊(cè)。
    // 如果使用tap注冊(cè)的話,進(jìn)行異步操作是不會(huì)等待異步操作執(zhí)行完成的。
    compiler.hooks.make.tap("TestPlugin", (compilation) => {
      setTimeout(() => {
        console.log("compiler.make() 111");
      }, 2000);
    });

    // 使用tapAsync、tapPromise注冊(cè),進(jìn)行異步操作會(huì)等異步操作做完再繼續(xù)往下執(zhí)行
    compiler.hooks.make.tapAsync("TestPlugin", (compilation, callback) => {
      setTimeout(() => {
        console.log("compiler.make() 222");
        // 必須調(diào)用
        callback();
      }, 1000);
    });

    compiler.hooks.make.tapPromise("TestPlugin", (compilation) => {
      console.log("compiler.make() 333");
      // 必須返回promise
      return new Promise((resolve) => {
        resolve();
      });
    });

    // 從文檔可知, emit 是 AsyncSeriesHook, 也就是異步串行鉤子,特點(diǎn)就是異步任務(wù)順序執(zhí)行
    compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {
      setTimeout(() => {
        console.log("compiler.emit() 111");
        callback();
      }, 3000);
    });

    compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {
      setTimeout(() => {
        console.log("compiler.emit() 222");
        callback();
      }, 2000);
    });

    compiler.hooks.emit.tapAsync("TestPlugin", (compilation, callback) => {
      setTimeout(() => {
        console.log("compiler.emit() 333");
        callback();
      }, 1000);
    });
  }
}

module.exports = TestPlugin;

啟動(dòng)調(diào)試

通過調(diào)試查看 compiler 和 compilation 對(duì)象數(shù)據(jù)情況。

package.json 配置指令

{
  "name": "source",
  "version": "1.0.0",
  "scripts": {
    "debug": "node --inspect-brk ./node_modules/webpack-cli/bin/cli.js"
  },
  "keywords": [],
  "author": "xiongjian",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.17.10",
    "@babel/preset-env": "^7.17.10",
    "css-loader": "^6.7.1",
    "loader-utils": "^3.2.0",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2"
  }
}

運(yùn)行指令

npm run debug

此時(shí)控制臺(tái)輸出以下內(nèi)容:

PS C:\Users\86176\Desktop\source> npm run debug

> source@1.0.0 debug
> node --inspect-brk ./node_modules/webpack-cli/bin/cli.js

Debugger listening on ws://127.0.0.1:9229/629ea097-7b52-4011-93a7-02f83c75c797
For help, see: https://nodejs.org/en/docs/inspecto

打開 Chrome 瀏覽器,F(xiàn)12 打開瀏覽器調(diào)試控制臺(tái)。

此時(shí)控制臺(tái)會(huì)顯示一個(gè)綠色的圖標(biāo)

調(diào)試控制臺(tái)

點(diǎn)擊綠色的圖標(biāo)進(jìn)入調(diào)試模式。

在需要調(diào)試代碼處用 debugger 打斷點(diǎn),代碼就會(huì)停止運(yùn)行,從而調(diào)試查看數(shù)據(jù)情況。

BannerWebpackPlugin

作用:給打包輸出文件添加注釋。

開發(fā)思路:

需要打包輸出前添加注釋:需要使用 compiler.hooks.emit 鉤子, 它是打包輸出前觸發(fā)。

如何獲取打包輸出的資源?compilation.assets 可以獲取所有即將輸出的資源文件。

實(shí)現(xiàn):文章來源地址http://www.zghlxwxcb.cn/news/detail-813437.html

// plugins/banner-webpack-plugin.js
class BannerWebpackPlugin {
  constructor(options = {}) {
    this.options = options;
  }

  apply(compiler) {
    // 需要處理文件
    const extensions = ["js", "css"];

    // emit是異步串行鉤子
    compiler.hooks.emit.tapAsync("BannerWebpackPlugin", (compilation, callback) => {
      // compilation.assets包含所有即將輸出的資源
      // 通過過濾只保留需要處理的文件
      const assetPaths = Object.keys(compilation.assets).filter((path) => {
        const splitted = path.split(".");
        return extensions.includes(splitted[splitted.length - 1]);
      });

      assetPaths.forEach((assetPath) => {
        const asset = compilation.assets[assetPath];

        const source = `/*
* Author: ${this.options.author}
*/\n${asset.source()}`;

        // 覆蓋資源
        compilation.assets[assetPath] = {
          // 資源內(nèi)容
          source() {
            return source;
          },
          // 資源大小
          size() {
            return source.length;
          },
        };
      });

      callback();
    });
  }
}

module.exports = BannerWebpackPlugin;

CleanWebpackPlugin

作用:在 webpack 打包輸出前將上次打包內(nèi)容清空。

開發(fā)思路:

  1. 如何在打包輸出前執(zhí)行?需要使用 compiler.hooks.emit 鉤子, 它是打包輸出前觸發(fā)。
  2. 如何清空上次打包內(nèi)容?
  • 獲取打包輸出目錄:通過 compiler 對(duì)象。
  • 通過文件操作清空內(nèi)容:通過 compiler.outputFileSystem 操作文件。

實(shí)現(xiàn):

// plugins/clean-webpack-plugin.js
class CleanWebpackPlugin {
  apply(compiler) {
    // 獲取操作文件的對(duì)象
    const fs = compiler.outputFileSystem;
    // emit是異步串行鉤子
    compiler.hooks.emit.tapAsync("CleanWebpackPlugin", (compilation, callback) => {
      // 獲取輸出文件目錄
      const outputPath = compiler.options.output.path;
      // 刪除目錄所有文件
      const err = this.removeFiles(fs, outputPath);
      // 執(zhí)行成功err為undefined,執(zhí)行失敗err就是錯(cuò)誤原因
      callback(err);
    });
  }

  removeFiles(fs, path) {
    try {
      // 讀取當(dāng)前目錄下所有文件
      const files = fs.readdirSync(path);

      // 遍歷文件,刪除
      files.forEach((file) => {
        // 獲取文件完整路徑
        const filePath = `${path}/${file}`;
        // 分析文件
        const fileStat = fs.statSync(filePath);
        // 判斷是否是文件夾
        if (fileStat.isDirectory()) {
          // 是文件夾需要遞歸遍歷刪除下面所有文件
          this.removeFiles(fs, filePath);
        } else {
          // 不是文件夾就是文件,直接刪除
          fs.unlinkSync(filePath);
        }
      });

      // 最后刪除當(dāng)前目錄
      fs.rmdirSync(path);
    } catch (e) {
      // 將產(chǎn)生的錯(cuò)誤返回出去
      return e;
    }
  }
}

module.exports = CleanWebpackPlugin;

AnalyzeWebpackPlugin

作用:分析 webpack 打包資源大小,并輸出分析文件。

開發(fā)思路:

在哪做? compiler.hooks.emit, 它是在打包輸出前觸發(fā),我們需要分析資源大小同時(shí)添加上分析后的 md 文件。

實(shí)現(xiàn):

// plugins/analyze-webpack-plugin.js
class AnalyzeWebpackPlugin {
  apply(compiler) {
    // emit是異步串行鉤子
    compiler.hooks.emit.tap("AnalyzeWebpackPlugin", (compilation) => {
      // Object.entries將對(duì)象變成二維數(shù)組。二維數(shù)組中第一項(xiàng)值是key,第二項(xiàng)值是value
      const assets = Object.entries(compilation.assets);

      let source = "# 分析打包資源大小 \n| 名稱 | 大小 |\n| --- | --- |";

      assets.forEach(([filename, file]) => {
        source += `\n| ${filename} | ${file.size()} |`;
      });

      // 添加資源
      compilation.assets["analyze.md"] = {
        source() {
          return source;
        },
        size() {
          return source.length;
        },
      };
    });
  }
}

module.exports = AnalyzeWebpackPlugin;

InlineChunkWebpackPlugin

作用:webpack 打包生成的 runtime 文件太小了,額外發(fā)送請(qǐng)求性能不好,所以需要將其內(nèi)聯(lián)到 js 中,從而減少請(qǐng)求數(shù)量。

開發(fā)思路:

1、我們需要借助 html-webpack-plugin 來實(shí)現(xiàn)

  • 在 html-webpack-plugin 輸出 index.html 前將內(nèi)聯(lián) runtime 注入進(jìn)去
  • 刪除多余的 runtime 文件

2、如何操作 html-webpack-plugin?官方文檔

實(shí)現(xiàn):

// plugins/inline-chunk-webpack-plugin.js
const HtmlWebpackPlugin = require("safe-require")("html-webpack-plugin");

class InlineChunkWebpackPlugin {
  constructor(tests) {
    this.tests = tests;
  }

  apply(compiler) {
    compiler.hooks.compilation.tap("InlineChunkWebpackPlugin", (compilation) => {
      const hooks = HtmlWebpackPlugin.getHooks(compilation);

      hooks.alterAssetTagGroups.tap("InlineChunkWebpackPlugin", (assets) => {
        assets.headTags = this.getInlineTag(assets.headTags, compilation.assets);
        assets.bodyTags = this.getInlineTag(assets.bodyTags, compilation.assets);
      });

      hooks.afterEmit.tap("InlineChunkHtmlPlugin", () => {
        Object.keys(compilation.assets).forEach((assetName) => {
          if (this.tests.some((test) => assetName.match(test))) {
            delete compilation.assets[assetName];
          }
        });
      });
    });
  }

  getInlineTag(tags, assets) {
    return tags.map((tag) => {
      if (tag.tagName !== "script") return tag;

      const scriptName = tag.attributes.src;

      if (!this.tests.some((test) => scriptName.match(test))) return tag;

      return { tagName: "script", innerHTML: assets[scriptName].source(), closeTag: true };
    });
  }
}

module.exports = InlineChunkWebpackPlugin;

到了這里,關(guān)于Webpack5入門到原理18:Plugin 原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Webpack5入門到原理21:提升開發(fā)體驗(yàn)

    開發(fā)時(shí)我們運(yùn)行的代碼是經(jīng)過 webpack 編譯后的,例如下面這個(gè)樣子: 所有 css 和 js 合并成了一個(gè)文件,并且多了其他代碼。此時(shí)如果代碼運(yùn)行出錯(cuò)那么提示代碼錯(cuò)誤位置我們是看不懂的。一旦將來開發(fā)代碼文件很多,那么很難去發(fā)現(xiàn)錯(cuò)誤出現(xiàn)在哪里。 所以我們需要更加準(zhǔn)確

    2024年01月25日
    瀏覽(17)
  • Webpack5入門到原理3:基本配置

    在開始使用 Webpack 之前,我們需要對(duì) Webpack 的配置有一定的認(rèn)識(shí)。 entry(入口) 指示 Webpack 從哪個(gè)文件開始打包 output(輸出) 指示 Webpack 打包完的文件輸出到哪里去,如何命名等 loader(加載器) webpack 本身只能處理 js、json 等資源,其他資源需要借助 loader,Webpack 才能解析

    2024年01月20日
    瀏覽(49)
  • Webpack5入門到原理5:處理樣式資源

    我們學(xué)習(xí)使用 Webpack 如何處理 Css、Less、Sass、Scss、Styl 樣式資源 Webpack 本身是不能識(shí)別樣式資源的,所以我們需要借助 Loader 來幫助 Webpack 解析樣式資源 我們找 Loader 都應(yīng)該去官方文檔中找到對(duì)應(yīng)的 Loader,然后使用 官方文檔找不到的話,可以從社區(qū) Github 中搜索查詢 Webpack 官

    2024年01月21日
    瀏覽(21)
  • Webpack5入門到原理14:生產(chǎn)模式介紹

    生產(chǎn)模式是開發(fā)完成代碼后,我們需要得到代碼將來部署上線。 這個(gè)模式下我們主要對(duì)代碼進(jìn)行優(yōu)化,讓其運(yùn)行性能更好。 優(yōu)化主要從兩個(gè)角度出發(fā): 優(yōu)化代碼運(yùn)行性能 優(yōu)化代碼打包速度 我們分別準(zhǔn)備兩個(gè)配置文件來放不同的配置 因?yàn)槲募夸涀兞?,所以所有絕對(duì)路徑需要

    2024年01月23日
    瀏覽(63)
  • Webpack5入門到原理11:處理 js 資源

    有人可能會(huì)問,js 資源 Webpack 不能已經(jīng)處理了嗎,為什么我們還要處理呢? 原因是 Webpack 對(duì) js 處理是有限的,只能編譯 js 中 ES 模塊化語法,不能編譯其他語法,導(dǎo)致 js 不能在 IE 等瀏覽器運(yùn)行,所以我們希望做一些兼容性處理。 其次開發(fā)中,團(tuán)隊(duì)對(duì)代碼格式是有嚴(yán)格要求的

    2024年01月20日
    瀏覽(55)
  • Webpack5入門到原理12:處理 Html 資源

    webpack.config.js 去掉引入的 js 文件,因?yàn)?HtmlWebpackPlugin 會(huì)自動(dòng)引入 此時(shí) dist 目錄就會(huì)輸出一個(gè) index.html 文件

    2024年01月23日
    瀏覽(24)
  • Webpack5入門到原理6:處理圖片資源

    過去在 Webpack4 時(shí),我們處理圖片資源通過 file-loader 和 url-loader 進(jìn)行處理 現(xiàn)在 Webpack5 已經(jīng)將兩個(gè) Loader 功能內(nèi)置到 Webpack 里了,我們只需要簡(jiǎn)單配置即可處理圖片資源 src/images/1.jpeg src/images/2.png src/images/3.gif src/less/index.less src/sass/index.sass src/styl/index.styl 打開 index.html 頁面查看

    2024年01月20日
    瀏覽(23)
  • Webpack5入門到原理22:提升打包構(gòu)建速度

    開發(fā)時(shí)我們修改了其中一個(gè)模塊代碼,Webpack 默認(rèn)會(huì)將所有模塊全部重新打包編譯,速度很慢。 所以我們需要做到修改某個(gè)模塊代碼,就只有這個(gè)模塊代碼需要重新打包編譯,其他模塊不變,這樣打包速度就能很快。 HotModuleReplacement(HMR/熱模塊替換):在程序運(yùn)行中,替換、

    2024年01月21日
    瀏覽(21)
  • Webpack5入門到原理19:React 腳手架搭建

    package.json .eslintrc.js babel.config.js webpack.config.js 修改運(yùn)行指令 package.json

    2024年01月21日
    瀏覽(28)
  • Webpack5入門到原理20:Vue 腳手架搭建

    package.json .eslintrc.js babel.config.js

    2024年01月24日
    瀏覽(48)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包