HMR в Webpack опирается на двунаправленный канал связи между сервером разработки и клиентским рантаймом в браузере, чаще всего реализованный через WebSocket. Этот канал передаёт структурированные сообщения, описывающие состояние сборки, появление обновлений модулей и инструкции по их применению. Протокол HMR не является отдельным стандартом, он представляет собой набор соглашений поверх WebSocket между webpack-dev-server (или совместимым middleware) и HMR runtime.
После запуска dev-сервера клиентский скрипт HMR устанавливает WebSocket-соединение с сервером. В этот момент формируется контекст сборки: клиент получает идентификатор текущей сборки (hash), который используется как точка синхронизации.
Основной поток сообщений строится вокруг следующих типов событий:
hashКаждая успешная сборка Webpack генерирует уникальный hash. Сервер отправляет его клиенту:
hash фиксирует состояние графа модулейКлиент сохраняет последний полученный hash и использует его как базу для запроса hot-update файлов.
okПосле завершения компиляции сервер отправляет сигнал:
На этом этапе runtime вызывает внутреннюю функцию проверки обновлений и обращается к update manifest.
content-changedИспользуется в сценариях, когда изменился контент, не затрагивающий JavaScript-граф напрямую:
Поведение клиента зависит от конфигурации:
errors и
warningsСервер передаёт массив диагностических сообщений компиляции:
errors блокируют применение обновленияwarnings допускают продолжение HMR-циклаКлиентский runtime:
При изменении модулей Webpack формирует два ключевых артефакта:
Сервер уведомляет клиента о наличии обновления через сообщение, содержащее:
Клиент после получения этого сообщения инициирует загрузку обновлений через обычные HTTP-запросы.
Типичная структура данных включает:
c — список изменённых чанковh — новый hashr — необходимость полной перезагрузкиp — публичный путь к ассетамЭта информация используется для построения URL:
/<publicPath>/<chunkId>.<hash>.hot-update.js/<publicPath>/<hash>.hot-update.jsonhotDownloadManifestПосле загрузки JSON-манifеста runtime получает список модулей, требующих обновления. Далее происходит переход к фазе загрузки hot-update чанков.
На этом этапе Webpack runtime:
hotDownloadUpdateChunkКаждый изменённый чанк загружается отдельно:
hotUpdateReadyПосле загрузки всех чанков runtime начинает фазу применения изменений:
hotApplymodule.hot и
события внутри модулейКаждый модуль может подписываться на HMR события через API:
module.hot.accept(deps, callback)module.hot.dispose(callback)module.hot.decline()Эти обработчики становятся частью внутреннего графа событий.
acceptСигнализирует, что модуль может быть обновлён без полной перезагрузки. При получении обновления:
disposeВызывается перед заменой модуля:
declineЗапрещает hot replacement:
После получения hot-update runtime строит граф распространения:
accept у каждого узлаЕсли хотя бы один путь не поддерживает HMR, инициируется полный reload.
hotApplyНа этом этапе происходит фактическая замена модулей в runtime-кэше Webpack:
Важно, что порядок применения строго детерминирован графом зависимостей.
hotDisposeПеред заменой каждого модуля вызываются dispose handlers:
module.hot.dataЭти данные затем доступны новому экземпляру модуля.
Если во время apply возникает ошибка:
Типичные причины:
Каждое обновление строго связано с предыдущим hash:
Это предотвращает применение устаревших патчей.
HMR runtime внедряется в bundle как bootstrap слой:
requireОн является посредником между Webpack bundle и dev-server протоколом.
При потере WebSocket-соединения:
Полный цикл сообщений выглядит следующим образом:
Каждый цикл связан с конкретной итерацией графа модулей, что позволяет обновлять приложение без перезапуска страницы и без потери состояния при корректной архитектуре модулей.