Hot Module Replacement (HMR) — механизм, позволяющий обновлять модули приложения во время работы без полной перезагрузки страницы. В контексте Vue.js HMR используется исключительно в режиме разработки и решает ключевую проблему фронтенд-разработки: медленный цикл «изменил код → перезагрузил страницу → восстановил состояние».
HMR работает на уровне модулей JavaScript, отслеживая изменения файлов и подменяя только те части приложения, которые были изменены. При этом состояние приложения (state, данные форм, положение прокрутки, открытые компоненты) может сохраняться.
В основе HMR лежит связка из нескольких компонентов:
Последовательность работы:
Если обновление невозможно применить безопасно, происходит fallback — полная перезагрузка.
Vue 3 изначально проектировался с учётом HMR, а Vite стал стандартным инструментом разработки.
Особенности реализации:
Пример структуры проекта:
src/
├─ main.js
├─ App.vue
└─ components/
└─ Counter.vue
При изменении Counter.vue Vite отправляет обновлённый
модуль, и Vue:
Файлы .vue состоят из нескольких логических частей:
<template><script><style>HMR обрабатывает их независимо:
<template> обновляют
рендер-функцию<script> обновляют логику
компонента<style> подменяют CSS без перерисовки
DOMЭто позволяет:
Vue пытается сохранить состояние компонента при HMR, если:
Пример состояния, сохраняемого при HMR:
data() {
return {
count: 5
}
}
При изменении шаблона count останется равным
5.
Состояние может быть потеряно, если:
setup() таким образом, что ломается
реактивностьpropsComposition API хорошо совместим с HMR, но имеет нюансы.
Код:
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
}
}
Изменение логики внутри setup() может привести к
пересозданию реактивных объектов. Vue старается восстановить состояние,
но гарантий нет.
Ключевые моменты:
ref и reactive могут быть пересозданыsetup() важны для
восстановленияГлобальное состояние обычно переживает HMR без проблем.
Pinia:
Пример Pinia с HMR:
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
}
}
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
Без этого блока состояние может быть сброшено при обновлении store.
Vite предоставляет API для ручной работы с HMR:
import.meta.hot.accept()import.meta.hot.dispose()Пример:
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
console.log('Модуль обновлён')
})
import.meta.hot.dispose(() => {
console.log('Модуль удаляется')
})
}
Используется для:
HMR не является абсолютным:
Типичные причины fallback-перезагрузки:
setup()CSS обрабатывается отдельно:
<style scoped> обновляется без перерендера
компонентаПреимущества:
Vue 2:
Vue 3:
Это стало возможным благодаря новой архитектуре реактивности и компилятора.
HMR напрямую влияет на:
Для крупных приложений с большим количеством состояния HMR становится не просто удобством, а необходимым инструментом, сокращающим время разработки и количество ошибок, связанных с потерей контекста.
TypeScript не мешает работе HMR:
.ts и .vue обрабатываются
одинаковоHMR обновляет скомпилированный JavaScript, а не типовую информацию.
Если при обновлении возникает runtime-ошибка:
Это позволяет быстро исправлять ошибки, не теряя состояние приложения.
Наличие HMR поощряет:
Код, устойчивый к HMR, как правило, лучше структурирован и проще поддерживается.