Ошибки при HMR и полная перезагрузка страницы

В основе Hot Module Replacement лежит граф модулей и система распространения обновлений от изменённых модулей вверх по цепочке зависимостей. При каждом изменении исходного кода webpack формирует новый chunk hash, сравнивает сборки и пытается применить различия к уже загруженному в браузере runtime.

Процесс обновления включает несколько этапов:

  • компиляция нового манифеста модулей
  • вычисление hot update chunks
  • загрузка обновлённых модулей через runtime HMR
  • попытка внедрения изменений в существующий граф модулей
  • распространение обновления через цепочку accept-хендлеров

Полная перезагрузка страницы становится крайним вариантом, когда хотя бы один из этапов нарушает целостность цепочки применения обновлений.


Типы ошибок, приводящих к сбою HMR

Ошибки компиляции и остановка hot pipeline

Если сборка завершается с ошибкой, HMR вообще не инициируется. В этом случае dev server переходит в режим ожидания корректной сборки, после чего может инициировать reload.

Типичные причины:

  • синтаксические ошибки JavaScript/TypeScript
  • ошибки резолва модулей
  • неверные loader-конфигурации
  • падение babel/ts-loader на трансформации

При этом webpack dev server различает два состояния:

  • build failed (нет обновления)
  • build succeeded with warnings (возможен HMR)

Runtime-ошибки, блокирующие применение hot update

Даже при успешной сборке обновление может быть отменено, если во время выполнения hot runtime возникает ошибка:

  • исключение при выполнении модуля
  • ошибка в hot.accept обработчике
  • несовместимость нового и старого состояния модуля
  • нарушение порядка инициализации зависимостей

Когда runtime фиксирует необрабатываемую ошибку, обновление помечается как «invalidated», и система часто инициирует full reload для восстановления консистентного состояния.


Отсутствие accept-цепочки

Одно из ключевых ограничений HMR — необходимость явного или косвенного принятия обновления.

Если модуль не определяет:

  • module.hot.accept()
  • или не находится в цепочке модулей с accept-хендлером выше по графу

то обновление считается «unaccepted».

В этом случае webpack runtime:

  • отменяет hot apply
  • помечает обновление как requiring full reload

Особенно часто это происходит в следующих ситуациях:

  • модуль является leaf-нодой без accept
  • изменение затрагивает entry point
  • отсутствует self-accepting boundary

Ошибки распространения обновлений по графу модулей

Разрыв цепочки accept-обработчиков

HMR работает через propagation graph: изменение поднимается от изменённого модуля к родителям, пока не встретит accept boundary.

Сбой возникает, если:

  • граф содержит циклы без accept
  • промежуточный модуль не поддерживает hot
  • зависимость заменена на несовместимую версию

В этом случае runtime фиксирует:

  • Aborted because module is not accepted

и переходит к полной перезагрузке.


Инвалидация модулей из-за несовместимости

При изменении интерфейса модуля (экспортов, типов, побочных эффектов) HMR может не суметь корректно применить патч.

Типичные случаи:

  • изменение named exports
  • замена CommonJS на ESM или наоборот
  • изменение singleton-состояния
  • добавление side effects в module initialization

Webpack не выполняет глубокий семантический анализ, поэтому несовместимость часто проявляется только в runtime и приводит к fallback.


Причины forced full page reload в webpack-dev-server

liveReload как резервный механизм

Если включён liveReload, dev server использует HMR как предпочтительный механизм, но при его невозможности инициирует reload страницы.

Сценарии:

  • HMR runtime не отвечает
  • websocket connection потерян
  • hot update не может быть применён

hotOnly и поведение при сбоях

Параметр hotOnly (или devServer.hot = true без liveReload) изменяет поведение:

  • при ошибке HMR обновление просто отклоняется
  • страница не перезагружается автоматически
  • требуется повторная сборка

В классической конфигурации без hotOnly fallback происходит автоматически.


Ошибки WebSocket соединения

HMR зависит от канала связи между браузером и dev server:

  • потеря соединения
  • несовпадение hash версий
  • таймаут heartbeat
  • прокси-обрыв (nginx, docker, reverse proxy)

При восстановлении соединения webpack сравнивает текущий hash и может инициировать full reload, если состояние runtime неизвестно.


Конфликты модулей и неконсистентное состояние

Разные версии одного модуля в рантайме

При частичном обновлении может возникнуть ситуация, когда:

  • часть модулей уже заменена
  • часть остаётся старой
  • зависимости пересекаются

Это приводит к состоянию «mixed graph», которое webpack считает небезопасным для hot apply.

Результат:

  • abort hot update
  • fallback to full reload

Ошибки в splitChunks и chunk boundary

При использовании code splitting возможны проблемы:

  • изменение общего чанка
  • несовпадение chunkId
  • invalidated shared dependency

Если общий chunk затрагивает множество entry points, HMR часто не может гарантировать корректное применение изменений.


Ошибки accept handlers и некорректная регистрация

Потеря accept handler из-за tree shaking

При оптимизациях сборки:

  • модуль может быть удалён как «неиспользуемый»
  • accept handler не попадает в runtime bundle

В результате HMR runtime не находит обработчик обновления.


Дублирование accept и конфликт обработки

Если несколько модулей объявляют конкурирующие accept handlers:

  • обновление может применяться частично
  • или отклоняться целиком

Webpack выбирает conservative strategy — при сомнении выполняется full reload.


Ошибки CSS HMR и побочные эффекты стилей

Хотя CSS HMR обычно стабильнее JS, ошибки возникают при:

  • конфликте mini-css-extract-plugin и style-loader
  • невозможности заменить extracted chunk
  • нарушении порядка injection rules

В таких случаях возможны:

  • утечка старых стилей
  • некорректная замена rules
  • принудительная перезагрузка страницы

Поведение при runtime crash после hot apply

Даже если обновление применилось успешно, следующий сценарий критичен:

  1. модуль обновлён
  2. код выполняется
  3. возникает исключение
  4. state runtime повреждён

Webpack runtime не выполняет rollback изменений, поэтому:

  • фиксируется fatal error state
  • запускается full reload как восстановление

Граф принятия обновлений и его деградация

HMR опирается на структуру:

  • module graph
  • parent-child relationships
  • accept boundaries

Деградация происходит, если:

  • граф слишком глубокий
  • отсутствуют явные boundaries
  • динамические import ломают статическую структуру

При невозможности построить путь принятия обновления применяется стратегия fallback.


Abort причины внутри HMR runtime

Внутренние причины отказа hot update:

  • Cannot apply update. Need full reload.
  • Module not accepted
  • Dispose handler failed
  • Update propagation failed
  • Aborted due to error in self-accepted module

Каждая из них указывает на невозможность безопасной инъекции изменений в существующий runtime без перезапуска страницы.


Стратегии минимизации перехода к полной перезагрузке

С точки зрения архитектуры модулей ключевым фактором является наличие устойчивых hot boundaries:

  • явные module.hot.accept на уровне компонентов
  • изоляция состояния вне модулей
  • минимизация глобальных singleton-состояний
  • разделение entry points на независимые области
  • контроль side effects при загрузке модулей

При нарушении этих принципов вероятность fallback к full reload резко возрастает, особенно в приложениях с большим числом взаимозависимостей и динамической загрузкой чанков