HMR API
注意
这是客户端 HMR API。如需在插件中处理 HMR 更新,请参阅 handleHotUpdate。
手动 HMR API 主要面向框架和工具作者。作为最终用户,HMR 很可能已经在框架特定的入门模板中为你处理好了。
Vite 通过特殊的 import.meta.hot 对象暴露其手动 HMR API
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 移除
if (import.meta.hot) {
// HMR code
}TypeScript 的智能感知
Vite 在 vite/client.d.ts 中为 import.meta.hot 提供了类型定义。你可以在 tsconfig.json 中添加 "vite/client",这样 TypeScript 就能识别这些类型定义
{
"compilerOptions": {
"types": ["vite/client"]
}
}hot.accept(cb)
若要让模块自接受,请使用带有回调函数的 import.meta.hot.accept,该回调函数会接收更新后的模块
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)。
Vite 的 HMR 实际上并不会交换原始导入的模块:如果 HMR 边界模块重新导出了依赖项的导入,那么它有责任更新这些导出(且这些导出必须使用 let 声明)。此外,边界模块上游的导入者将不会收到更改通知。这种简化的 HMR 实现足以满足大多数开发用例,同时也让我们免去了生成代理模块的繁重工作。
Vite 要求此函数的调用在源代码中表现为 import.meta.hot.accept((注意空格敏感),模块才能接受更新。这是 Vite 为模块启用 HMR 支持所进行的静态分析要求。
hot.accept(deps, cb)
模块也可以接受来自直接依赖项的更新,而无需重新加载自身
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 来清理其更新副本所创建的任何持久副作用
function setupSideEffect() {}
setupSideEffect()
if (import.meta.hot) {
import.meta.hot.dispose((data) => {
// cleanup side effect
})
}hot.prune(cb)
注册一个回调,当模块不再被页面导入时调用。与 hot.dispose 相比,如果源代码在更新时自行清理副作用,且你仅需要在模块从页面中移除时进行清理,则可以使用此方法。Vite 目前将其用于 .css 导入。
function setupOrReuseSideEffect() {}
setupOrReuseSideEffect()
if (import.meta.hot) {
import.meta.hot.prune((data) => {
// cleanup side effect
})
}hot.data
import.meta.hot.data 对象在同一个已更新模块的不同实例之间持久保存。它可用于将信息从模块的旧版本传递到新版本。
请注意,不支持重新赋值 data 本身。相反,你应该修改 data 对象的属性,以便保留来自其他处理程序的信息。
// 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,如下所示:
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 及其底层工作原理的信息,请查看以下资源:
