hot
2023年05月27日
一、认识
Vite
通过特殊的 import.meta.hot
对象暴露手动 HMR API
。
interface ImportMeta {
readonly hot?: ViteHotContext
}
type ModuleNamespace = Record<string, any> & {
[Symbol.toStringTag]: 'Module'
}
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
// `InferCustomEventPayload` provides types for built-in Vite events
on<T extends string>(
event: T,
cb: (payload: InferCustomEventPayload<T>) => void,
): void
off<T extends string>(
event: T,
cb: (payload: InferCustomEventPayload<T>) => void,
): void
send<T extends string>(event: T, data?: InferCustomEventPayload<T>): void
}
1.1 条件守卫
首先,请确保用一个条件语句守护所有 HMR API
的使用,这样代码就可以在生产环境中被 tree-shaking
优化:
if (import.meta.hot) {
// HMR 代码
}
二、hot.accept(cb)
hot.accept
用来接受模块更新, 一旦 Vite
接受了这个更新,当前模块就会被认为是 HMR
的边界。那么,Vite
接受谁的更新呢?这里会有三种情况:
-
接受自身模块的更新
if (import.meta.hot) {
import.meta.hot.accept((mod) => mod.render())
} -
接受某个子模块的更新
if (import.meta.hot) {
import.meta.hot.accept('./render.ts', (newModule) => {
newModule.render();
})
} -
接受多个子模块的更新
if (import.meta.hot) {
import.meta.hot.accept(['./render.ts', './state.ts'], (modules) => {
console.log(modules);
})
}
二、 hot.dispose(cb)
hot.dispose(cb)
来清除任何由其更新副本产生的持久副作用
if (import.meta.hot) {
import.meta.hot.dispose((data) => {
// 清理副作用
})
}
三、hot.prune(cb)
hot.prune(cb)
注册一个回调,当模块在页面上不再被导入时调用。与 hot.dispose
相比,如果源代码更新时自行清理了副作用,你只需要在模块从页面上被删除时,使用此方法进行清理。Vite
目前在 .css
导入上使用此方法。
if (import.meta.hot) {
import.meta.hot.prune((data) => {
// 清理副作用
})
}
四、hot.data
hot.data
在同一个更新模块的不同实例之间持久化。它可以用于将信息从模块的前一个版本传递到下一个版本。注意,不支持对 data
本身的重新赋值。相反,你应该对 data
对象的属性进行突变,以便保留从其他处理程序添加的信息。
import.meta.hot.data.someValue = 'hello'