成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺析Vite插件機(jī)制,你學(xué)會(huì)了嗎?

開發(fā) 前端
對(duì)于Vite來說,它是基于esbuild與rollup?雙引擎設(shè)計(jì)的,在開發(fā)階段使用esbuild?進(jìn)行依賴預(yù)構(gòu)建,然后基于瀏覽器原生支持的ESM?完成開發(fā)預(yù)覽,而在生產(chǎn)環(huán)境打包時(shí),直接使用的rollup?構(gòu)建。那么在這種背景下,Vite的插件機(jī)制應(yīng)該如何設(shè)計(jì)?

前言

對(duì)于Vite來說,它是基于esbuild與rollup雙引擎設(shè)計(jì)的,在開發(fā)階段使用esbuild進(jìn)行依賴預(yù)構(gòu)建,然后基于瀏覽器原生支持的ESM完成開發(fā)預(yù)覽,而在生產(chǎn)環(huán)境打包時(shí),直接使用的rollup構(gòu)建。那么在這種背景下,Vite的插件機(jī)制應(yīng)該如何設(shè)計(jì)?

在源碼中,我們能夠經(jīng)常看到PluginContainer的身影,Vite正是通過它來模擬rollup的行為

圖片

pluginContainer

PluginContainer 的 實(shí)現(xiàn) 基于借鑒于 WMR 中的 rollup-plugin-container.js ,主要功能有兩個(gè):

  • 管理插件的生命周期
  • 實(shí)現(xiàn)插件鉤子內(nèi)部的 Context 上下文對(duì)象

插件生命周期

在開發(fā)階段,vite會(huì)模擬rollup的行為,所以插件的執(zhí)行機(jī)制也與rollup相同

圖片圖片

  1. 調(diào)用 options 鉤子進(jìn)行配置的轉(zhuǎn)換,得到處理后的配置對(duì)象。
  2. 調(diào)用buildStart鉤子,正式開始構(gòu)建流程。
  3. 調(diào)用 resolveId 鉤子中解析文件路徑。(從 input 配置指定的入口文件開始)。
  4. 調(diào)用load鉤子加載模塊內(nèi)容。
  5. 緊接著 Rollup 執(zhí)行所有的 transform 鉤子來對(duì)模塊內(nèi)容進(jìn)行進(jìn)行自定義的轉(zhuǎn)換(比如 babel 轉(zhuǎn)譯)
  6. Rollup 拿到最后的模塊內(nèi)容,進(jìn)行 AST 分析,得到所有的 import 內(nèi)容,調(diào)用 moduleParsed 鉤子
  7. 直到所有的 import 都解析完畢,Rollup 執(zhí)行buildEnd鉤子,Build 階段結(jié)束。

?

這里需要注意的是:在 vite 中由于 AST 分析是通過 esbuild 進(jìn)行的,所有沒有模擬 moduleParsed 鉤子

傳遞上下文對(duì)象

上下文對(duì)象通過 Context 實(shí)現(xiàn) PluginContext 接口定義,PluginContext 實(shí)際上是 Rollup 內(nèi)部定義的類型,可以在源碼中看到 vite 實(shí)現(xiàn)了 Rollup 上下文對(duì)象

class Context implements PluginContext {
  //... 具體實(shí)現(xiàn)
}

type PluginContext = Omit<
  RollupPluginContext, // Rollup 定義插件上下文接口
  // not documented
  | 'cache'
  // deprecated
  | 'moduleIds'
>

Context 上下文對(duì)象一共有 14 個(gè)核心方法,其中有 3 個(gè)方法是比較核心的方法

  • parse:使用 Rollup 的內(nèi)部基于 SWC 的解析器將代碼解析為 AST
  • resolve:將相對(duì)路徑解析為絕對(duì)路徑,從而正確地處理模塊之間的引用
  • load:加載并解析與給定 ID 對(duì)應(yīng)的模塊,并在提供的情況下附加附加的元信息到模塊

更多內(nèi)容可以查看rollup文檔

rollup插件

rollup構(gòu)建流程主要分為兩大類:build和output,build 階段主要負(fù)責(zé)創(chuàng)建模塊依賴圖,初始化各個(gè)模塊的 AST 以及模塊之間的依賴關(guān)系。output階段才是真正的打包構(gòu)建過程

插件hook類型(構(gòu)建階段)

通過構(gòu)建流程rollup的hook類型可以分為:build hook和output hook兩大類

  • Build Hook即在Build階段執(zhí)行的鉤子函數(shù),在這個(gè)階段主要進(jìn)行模塊代碼的轉(zhuǎn)換、AST 解析以及模塊依賴的解析,那么這個(gè)階段的 Hook 對(duì)于代碼的操作粒度一般為模塊級(jí)別,也就是單文件級(jí)別。
  • Ouput Hook(官方稱為Output Generation Hook),則主要進(jìn)行代碼的打包,對(duì)于代碼而言,操作粒度一般為 chunk級(jí)別(一個(gè) chunk 通常指很多文件打包到一起的產(chǎn)物)。

插件hook類型(執(zhí)行方式)

除了上面這種分類,rollup插件還可以根據(jù)各自的執(zhí)行方式來進(jìn)行分類:

  • Build Hook即在Build階段執(zhí)行的鉤子函數(shù),在這個(gè)階段主要進(jìn)行模塊代碼的轉(zhuǎn)換、AST 解析以及模塊依賴的解析,那么這個(gè)階段的 Hook 對(duì)于代碼的操作粒度一般為模塊級(jí)別,也就是單文件級(jí)別。
  • Ouput Hook(官方稱為Output Generation Hook),則主要進(jìn)行代碼的打包,對(duì)于代碼而言,操作粒度一般為 chunk級(jí)別(一個(gè) chunk 通常指很多文件打包到一起的產(chǎn)物)。

除了根據(jù)構(gòu)建階段可以將 Rollup 插件進(jìn)行分類,根據(jù)不同的 Hook 執(zhí)行方式也會(huì)有不同的分類,主要包括Async、Sync、Parallel、Squential、First這五種。在后文中我們將接觸各種各樣的插件 Hook,但無論哪個(gè) Hook 都離不開這五種執(zhí)行方式。

1. Async & Sync

首先是Async和Sync鉤子函數(shù),兩者其實(shí)是相對(duì)的,分別代表異步和同步的鉤子函數(shù),這個(gè)很好理解。

2. Parallel

這里指并行的鉤子函數(shù)。如果有多個(gè)插件實(shí)現(xiàn)了這個(gè)鉤子的邏輯,一旦有鉤子函數(shù)是異步邏輯,則并發(fā)執(zhí)行鉤子函數(shù),不會(huì)等待當(dāng)前鉤子完成(底層使用 Promise.all)。

比如對(duì)于Build階段的buildStart鉤子,它的執(zhí)行時(shí)機(jī)其實(shí)是在構(gòu)建剛開始的時(shí)候,各個(gè)插件可以在這個(gè)鉤子當(dāng)中做一些狀態(tài)的初始化操作,但其實(shí)插件之間的操作并不是相互依賴的,也就是可以并發(fā)執(zhí)行,從而提升構(gòu)建性能。反之,對(duì)于需要依賴其他插件處理結(jié)果的情況就不適合用 Parallel 鉤子了,比如 transform。

3. Sequential

Sequential 指串行的鉤子函數(shù)。這種 Hook 往往適用于插件間處理結(jié)果相互依賴的情況,前一個(gè)插件 Hook 的返回值作為后續(xù)插件的入?yún)ⅲ@種情況就需要等待前一個(gè)插件執(zhí)行完 Hook,獲得其執(zhí)行結(jié)果,然后才能進(jìn)行下一個(gè)插件相應(yīng) Hook 的調(diào)用,如transform。

4. First

如果有多個(gè)插件實(shí)現(xiàn)了這個(gè) Hook,那么 Hook 將依次運(yùn)行,直到返回一個(gè)非 null 或非 undefined 的值為止。比較典型的 Hook 是 resolveId,一旦有插件的 resolveId 返回了一個(gè)路徑,將停止執(zhí)行后續(xù)插件的 resolveId 邏輯。

通用hook

以下鉤子在服務(wù)器啟動(dòng)時(shí)被調(diào)用:

  • options
  • buildStart

以下鉤子會(huì)在每個(gè)傳入模塊請(qǐng)求時(shí)被調(diào)用:

  • resolveId
  • load
  • transform

它們還有一個(gè)擴(kuò)展的 options 參數(shù),包含其他特定于 Vite 的屬性。

以下鉤子在服務(wù)器關(guān)閉時(shí)被調(diào)用:

  • buildEnd
  • closeBundle

請(qǐng)注意 moduleParsed 鉤子在開發(fā)中是 不會(huì) 被調(diào)用的,因?yàn)?Vite 為了性能會(huì)避免完整的 AST 解析。

output階段的hook(除了 closeBundle) 在開發(fā)中是 不會(huì) 被調(diào)用的。你可以認(rèn)為 Vite 的開發(fā)服務(wù)器只調(diào)用了 rollup.rollup() 而沒有調(diào)用 bundle.generate()。

Vite 獨(dú)有hook

Vite 插件也可以提供鉤子來服務(wù)于特定的 Vite 目標(biāo)。當(dāng)然這些鉤子會(huì)被 Rollup 忽略。

config

在解析 Vite 配置前調(diào)用。鉤子接收原始用戶配置(命令行選項(xiàng)指定的會(huì)與配置文件合并)和一個(gè)描述配置環(huán)境的變量,包含正在使用的 mode 和 command。它可以返回一個(gè)將被深度合并到現(xiàn)有配置中的部分配置對(duì)象,或者直接改變配置(如果默認(rèn)的合并不能達(dá)到預(yù)期的結(jié)果)。

// 返回部分配置(推薦)
const partialConfigPlugin = () => ({
  name: 'nanjiu-plugin',
  config(config, { command }) {
    console.log('config', config, command)
  }
})

圖片圖片

需要注意的是:用戶插件在運(yùn)行這個(gè)鉤子之前會(huì)被解析,因此在 config 鉤子中注入其他插件不會(huì)有任何效果。

configResolved

在解析 Vite 配置后調(diào)用。使用這個(gè)鉤子讀取和存儲(chǔ)最終解析的配置。

const examplePlugin = () => {
  let config

  return {
    name: 'read-config',

    configResolved(resolvedConfig) {
      // 存儲(chǔ)最終解析的配置
      config = resolvedConfig
    },

    // 在其他鉤子中使用存儲(chǔ)的配置
    transform(code, id) {
      if (config.command === 'serve') {
        // dev: 由開發(fā)服務(wù)器調(diào)用的插件
      } else {
        // build: 由 Rollup 調(diào)用的插件
      }
    },
  }
}

configureServer

是用于配置開發(fā)服務(wù)器的鉤子。最常見的用例是在內(nèi)部 connect 應(yīng)用程序中添加自定義中間件

const myPlugin = () => ({
  name: 'configure-server',
  configureServer(server) {
    server.middlewares.use((req, res, next) => {
      // 自定義請(qǐng)求處理...
    })
  },
})

configurePreviewServer

與 configureServer 相同,但用于預(yù)覽服務(wù)器。configurePreviewServer 這個(gè)鉤子與 configureServer 類似,也是在其他中間件安裝前被調(diào)用。如果你想要在其他中間件 之后 安裝一個(gè)插件,你可以從 configurePreviewServer 返回一個(gè)函數(shù),它將會(huì)在內(nèi)部中間件被安裝之后再調(diào)用

const myPlugin = () => ({
  name: 'configure-preview-server',
  configurePreviewServer(server) {
    // 返回一個(gè)鉤子,會(huì)在其他中間件安裝完成后調(diào)用
    return () => {
      server.middlewares.use((req, res, next) => {
        // 自定義處理請(qǐng)求 ...
      })
    }
  },
})

transformIndexHtml

轉(zhuǎn)換 index.html 的專用鉤子。鉤子接收當(dāng)前的 HTML 字符串和轉(zhuǎn)換上下文。上下文在開發(fā)期間暴露ViteDevServer實(shí)例,在構(gòu)建期間暴露 Rollup 輸出的包。

這個(gè)鉤子可以是異步的,并且可以返回以下其中之一:

  • 經(jīng)過轉(zhuǎn)換的 HTML 字符串
  • 注入到現(xiàn)有 HTML 中的標(biāo)簽描述符對(duì)象數(shù)組({ tag, attrs, children })。每個(gè)標(biāo)簽也可以指定它應(yīng)該被注入到哪里(默認(rèn)是在 <head> 之前)
  • 一個(gè)包含 { html, tags } 的對(duì)象

默認(rèn)情況下 order 是 undefined,這個(gè)鉤子會(huì)在 HTML 被轉(zhuǎn)換后應(yīng)用。為了注入一個(gè)應(yīng)該通過 Vite 插件管道的腳本, order: 'pre' 指將在處理 HTML 之前應(yīng)用。order: 'post' 是在所有未定義的 order 的鉤子函數(shù)被應(yīng)用后才應(yīng)用。

const htmlPlugin = () => {
  return {
    name: 'nanjiu-plugin',
    transformIndexHtml(html) {
      return html.replace(/<title>(.*?)<\/title>/,
                          `<title> nanjiu plugin </title>`)
    },
  }
}

handleHotUpdate

執(zhí)行自定義 HMR 更新處理。鉤子接收一個(gè)帶有以下簽名的上下文對(duì)象

interface HmrContext {
  file: string
  timestamp: number
  modules: Array<ModuleNode>
  read: () => string | Promise<string>
  server: ViteDevServer
}
  • modules 是受更改文件影響的模塊數(shù)組。它是一個(gè)數(shù)組,因?yàn)閱蝹€(gè)文件可能映射到多個(gè)服務(wù)模塊(例如 Vue 單文件組件)。
  • read 這是一個(gè)異步讀函數(shù),它返回文件的內(nèi)容。之所以這樣做,是因?yàn)樵谀承┫到y(tǒng)上,文件更改的回調(diào)函數(shù)可能會(huì)在編輯器完成文件更新之前過快地觸發(fā),并 fs.readFile 直接會(huì)返回空內(nèi)容。傳入的 read 函數(shù)規(guī)范了這種行為。
const hotPlugin = () => {
  return {
    name: 'nanjiu-plugin',
    handleHotUpdate({ server, modules, timestamp}) {
      console.log('handleHotUpdate', modules)
    },
  }
}

當(dāng)我修改App.vue文件時(shí),modules可以獲取到如下信息:

圖片圖片

插件順序

一個(gè) Vite 插件可以額外指定一個(gè) enforce 屬性(類似于 webpack 加載器)來調(diào)整它的應(yīng)用順序。enforce 的值可以是pre 或 post。解析后的插件將按照以下順序排列:

  • Alias
  • 帶有 enforce: 'pre' 的用戶插件
  • Vite 核心插件
  • 沒有 enforce 值的用戶插件
  • Vite 構(gòu)建用的插件
  • 帶有 enforce: 'post' 的用戶插件
  • Vite 后置構(gòu)建插件

請(qǐng)注意,這與鉤子的排序是分開的,鉤子的順序仍然會(huì)受到它們的 order 屬性的影響,這一點(diǎn) 和 Rollup 鉤子的表現(xiàn)一樣

總結(jié)

vite 在 開發(fā)環(huán)境中,會(huì)使用 createPluginContainer 方法創(chuàng)建插件容器,插件容器有兩個(gè)核心功能:管理插件生命周期、傳遞插件上下文

圖片圖片

責(zé)任編輯:武曉燕 來源: 前端南玖
相關(guān)推薦

2024-04-28 08:24:27

分布式架構(gòu)Istio

2023-06-26 00:01:11

2023-05-04 08:01:35

umi 插件開發(fā)插件

2023-08-01 12:51:18

WebGPT機(jī)器學(xué)習(xí)模型

2024-01-02 12:05:26

Java并發(fā)編程

2024-05-11 09:03:26

數(shù)據(jù)表級(jí)鎖事務(wù)

2021-06-22 06:52:46

Vite 插件機(jī)制Rollup

2024-01-19 08:25:38

死鎖Java通信

2023-01-10 08:43:15

定義DDD架構(gòu)

2024-02-04 00:00:00

Effect數(shù)據(jù)組件

2023-07-26 13:11:21

ChatGPT平臺(tái)工具

2024-03-06 08:28:16

設(shè)計(jì)模式Java

2022-06-16 07:50:35

數(shù)據(jù)結(jié)構(gòu)鏈表

2022-12-06 07:53:33

MySQL索引B+樹

2023-01-31 08:02:18

2023-10-06 14:49:21

SentinelHystrixtimeout

2022-07-13 08:16:49

RocketMQRPC日志

2023-05-05 06:54:07

MySQL數(shù)據(jù)查詢

2023-03-26 22:31:29

2023-07-30 22:29:51

BDDMockitoAssert測(cè)試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产成人免费在线 | 亚洲精品国产a久久久久久 中文字幕一区二区三区四区五区 | 日韩不卡一区二区三区 | 我要看黄色录像一级片 | 青青99| 五月激情婷婷在线 | 亚洲美女网站 | 日韩欧美大片在线观看 | 在线观看视频91 | 精品国产91 | 99reav| 操久久 | 99热国产在线播放 | 午夜视频在线免费观看 | 国产精品三级 | 国产一区二区在线免费观看 | 成人三级视频 | 欧美国产日本一区 | 精品一区二区三区视频在线观看 | 中文字幕一区在线观看视频 | 98久久| 91在线精品视频 | 亚洲精品视频在线 | 亚洲福利精品 | 一区二区三区免费 | 精品少妇一区二区三区日产乱码 | 日韩视频免费 | 奇米四色影视 | 久久久激情视频 | 国产精品伦一区二区三级视频 | 国产精品国产三级国产aⅴ无密码 | 久久99精品国产麻豆婷婷 | 日韩精品在线播放 | 天天干天天爱天天操 | 中文字幕视频在线观看 | 成人不卡视频 | 欧美一区2区三区4区公司 | 黄色大片免费网站 | 第一av| 久久69精品久久久久久久电影好 | 国产在线视频三区 |