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 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
}
必須条件付きガード
まず、すべてのHMR APIの使用を条件付きブロックでガードして、本番環境でコードをツリーシェイクできるようにしてください。
if (import.meta.hot) {
// HMR code
}
TypeScriptのIntelliSense
Viteは、vite/client.d.ts
でimport.meta.hot
の型定義を提供しています。TypeScriptが型定義を拾うように、src
ディレクトリにenv.d.ts
を作成できます。
/// <reference 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境界**とみなされます。
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()
これは現在noopであり、下位互換性のために存在します。将来、新しい使用方法があれば変更される可能性があります。モジュールがホットアップデート可能ではないことを示すには、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の使用方法と内部動作の詳細を知りたい場合は、これらのリソースを確認してください。