跳轉到內容

HMR API

注意

這是客戶端 HMR API。如需在外掛中處理 HMR 更新,請參閱 handleHotUpdate

手動 HMR API 主要面向框架和工具作者。作為終端使用者,HMR 很可能已經在框架特定的入門模板中為你處理好了。

Vite 透過特殊的 import.meta.hot 物件暴露其手動 HMR API

ts
interface ImportMeta {
  readonly 
hot
?: ViteHotContext
} interface ViteHotContext { readonly
data
: any
accept
(): void
accept
(
cb
: (
mod
:
ModuleNamespace
| undefined) => void): void
accept
(
dep
: string,
cb
: (
mod
:
ModuleNamespace
| undefined) => void): void
accept
(
deps
: readonly string[],
cb
: (
mods
:
Array
<
ModuleNamespace
| undefined>) => void,
): void
dispose
(
cb
: (
data
: any) => void): void
prune
(
cb
: (
data
: any) => void): void
invalidate
(
message
?: string): void
on
<
T
extends
CustomEventName
>(
event
:
T
,
cb
: (
payload
:
InferCustomEventPayload
<
T
>) => void,
): void
off
<
T
extends
CustomEventName
>(
event
:
T
,
cb
: (
payload
:
InferCustomEventPayload
<
T
>) => void,
): void
send
<
T
extends
CustomEventName
>(
event
:
T
,
data
?:
InferCustomEventPayload
<
T
>,
): void }

強制條件守衛

首先,請確保使用條件程式碼塊保護所有的 HMR API 使用,這樣程式碼在生產環境中就可以被 tree-shaking 移除

js
if (import.meta.hot) {
  // HMR code
}

TypeScript 的智慧感知

Vite 在 vite/client.d.ts 中為 import.meta.hot 提供了型別定義。你可以在 tsconfig.json 中新增 "vite/client",這樣 TypeScript 就能識別這些型別定義

tsconfig.json
json
{
  "compilerOptions": {
    "types": ["vite/client"]
  }
}

hot.accept(cb)

若要讓模組自接受,請使用帶有回撥函式的 import.meta.hot.accept,該回調函式會接收更新後的模組

js
export const 
count
= 1
if (import.meta.
hot
) {
import.meta.
hot
.
accept
((
newModule
) => {
if (
newModule
) {
// newModule is undefined when SyntaxError happened
console
.
log
('updated: count is now ',
newModule
.count)
} }) }

一個“接受”熱更新的模組被視為一個 HMR 邊界 (HMR boundary)

hmr_boundary 建置工具root 建置工具main.jsparent 建置工具App.vueroot->parent 建置工具importsboundary 建置工具Component.vue(HMR boundary)hot.accept()parent->boundary 建置工具importsedited 建置工具utils.js(edited)boundary->edited 建置工具imports
hmr_boundary 建置工具root 建置工具main.jsparent 建置工具App.vueroot->parent 建置工具importsboundary 建置工具Component.vue(HMR boundary)hot.accept()parent->boundary 建置工具importsedited 建置工具utils.js(edited)boundary->edited 建置工具imports

Vite 的 HMR 實際上並不會交換原始匯入的模組:如果 HMR 邊界模組重新匯出了依賴項的匯入,那麼它有責任更新這些匯出(且這些匯出必須使用 let 宣告)。此外,邊界模組上游的匯入者將不會收到更改通知。這種簡化的 HMR 實現足以滿足大多數開發用例,同時也讓我們免去了生成代理模組的繁重工作。

Vite 要求此函式的呼叫在原始碼中表現為 import.meta.hot.accept((注意空格敏感),模組才能接受更新。這是 Vite 為模組啟用 HMR 支援所進行的靜態分析要求。

hot.accept(deps, cb)

模組也可以接受來自直接依賴項的更新,而無需重新載入自身

js
import { 
foo
} from './foo.js'
foo
()
if (import.meta.
hot
) {
import.meta.
hot
.
accept
('./foo.js', (
newFoo
) => {
// the callback receives the updated './foo.js' module
newFoo
?.foo()
}) // Can also accept an array of dep modules: import.meta.
hot
.
accept
(
['./foo.js', './bar.js'], ([
newFooModule
,
newBarModule
]) => {
// The callback receives an array where only the updated module is // non null. If the update was not successful (syntax error for ex.), // the array is empty }, ) }

hot.dispose(cb)

自接受模組或預期被其他模組接受的模組,可以使用 hot.dispose 來清理其更新副本所建立的任何持久副作用

js
function 
setupSideEffect
() {}
setupSideEffect
()
if (import.meta.
hot
) {
import.meta.
hot
.
dispose
((
data
) => {
// cleanup side effect }) }

hot.prune(cb)

註冊一個回撥,當模組不再被頁面匯入時呼叫。與 hot.dispose 相比,如果原始碼在更新時自行清理副作用,且你僅需要在模組從頁面中移除時進行清理,則可以使用此方法。Vite 目前將其用於 .css 匯入。

js
function 
setupOrReuseSideEffect
() {}
setupOrReuseSideEffect
()
if (import.meta.
hot
) {
import.meta.
hot
.
prune
((
data
) => {
// cleanup side effect }) }

hot.data

import.meta.hot.data 物件在同一個已更新模組的不同例項之間持久儲存。它可用於將資訊從模組的舊版本傳遞到新版本。

請注意,不支援重新賦值 data 本身。相反,你應該修改 data 物件的屬性,以便保留來自其他處理程式的資訊。

js
// ok
import.meta.
hot
.
data
.someValue = 'hello'
// not supported import.meta.
hot
.
data
= {
someValue
: 'hello' }

hot.decline()

此函式目前是一個空操作,僅為向後相容而保留。如果未來有新的用途,這種情況可能會改變。要指示模組不可熱更新,請使用 hot.invalidate()

hot.invalidate(message?: string)

自接受模組可能會在執行時意識到它無法處理 HMR 更新,因此需要強制將更新傳播給匯入者。透過呼叫 import.meta.hot.invalidate(),HMR 伺服器將使呼叫者的匯入者失效,就像呼叫者不是自接受的一樣。這會在瀏覽器控制檯和終端中記錄一條訊息。你可以傳遞一條訊息來提供關於為何發生失效的上下文資訊。

請注意,即使你打算緊接著呼叫 invalidate,也應該始終呼叫 import.meta.hot.accept,否則 HMR 客戶端將不會監聽該自接受模組的後續更改。為了清晰地表達意圖,我們建議在 accept 回撥中呼叫 invalidate,如下所示:

js
import.meta.
hot
.
accept
((
module
) => {
// You may use the new module instance to decide whether to invalidate. if (cannotHandleUpdate(
module
)) {
import.meta.
hot
.
invalidate
()
} })

hot.on(event, cb)

監聽 HMR 事件。

以下 HMR 事件由 Vite 自動排程

  • 'vite:beforeUpdate':當更新即將應用時(例如模組將被替換)
  • 'vite:afterUpdate':當更新剛剛應用後(例如模組已被替換)
  • 'vite:beforeFullReload':當即將發生全頁過載時
  • 'vite:beforePrune':當不再需要的模組即將被清理時
  • 'vite:invalidate':當模組透過 import.meta.hot.invalidate() 失效時
  • 'vite:error':當發生錯誤時(例如語法錯誤)
  • 'vite:ws:disconnect':當 WebSocket 連線丟失時
  • 'vite:ws:connect':當 WebSocket 連線(重新)建立時

外掛也可以傳送自定義 HMR 事件。更多詳細資訊,請參閱 handleHotUpdate

hot.off(event, cb)

從事件監聽器中移除回撥。

hot.send(event, data)

將自定義事件傳送回 Vite 開發伺服器。

如果在連線建立前呼叫,資料將被緩衝,並在連線建立後傳送。

有關更多詳細資訊(包括自定義事件的型別定義章節),請參閱客戶端-伺服器通訊

深入閱讀

如果你想了解更多關於如何使用 HMR API 及其底層工作原理的資訊,請檢視以下資源: