TGViewer
Виталий и Платформа Виталий и Платформа @web_platform · 1.81K subscribers
Post #15 159
Hot Module Replacement на нативных ES-модулях, часть 1

#инструменты #JS #Лаборатория_веб_платформы

Представьте, что вы начинаете разрабатывать JS-приложение. При разработке вы используете локальный сервер, запускаете проект, далее собирается JS-бандл и приложение открывается в браузере. В вашем проекте пока что немного модулей, и приложение «собирается» быстро. Правда в процессе разработки вам приходится полностью обновлять страницу, чтобы увидеть, как работают ваши написанные скрипты, и состояние приложения теряется при перезагрузке. Со временем также модулей в приложении становится много и полная «пересборка» теперь длится долго.

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

Ок, сделали. Перебилд теперь проихоходит быстрее, но страницу всё ещё приходится полностью перезагружать для обновления. Теперь задача в том, чтобы точечно обновить загруженный код в вашем браузере с запущенным сервером. Как это сделать «на горячую», без перезагрузки всей страницы целиком? Для этого придуман подход Hot Module Replacement (HMR).

HMR — это способ точечного обновления в браузере только изменившихся JS-модулей приложения вместо полной перезагрузки страницы. Дальше я рассмотрю, как HMR реализован в сборщиках на нативных ES-модулях — например, в Snowpack или Vite.

В HMR-движке на нативных ES-модулях есть две части: серверная и клиентская.

На сервере из всех модулей и их зависимостей (import и export) строится дерево: в корне — «главный» модуль (что-то типа app.js), от него расходится древовидная цепочка подключаемых JS-модулей, от которых модуль зависит.

Также на сервере стороне подготавливаются все файлы модулей и запускается WebSocket Server, который будет ждать сообщения от клиента, с запросом на пересборку дерева или на перезагрузку страницы целиком.

Клиент подключается к серверу по WebSocket. Когда изменяется один из файлов JS-модулей, от клиента на сервер отправляется сообщение, какой именно модуль изменился.

Когда сервер получает сообщение от клиента, он начинает анализировать зависимости этого модуля. Сервер идёт вверх по цепочке родительских зависимостей и инвалидирует их до тех пор, пока не дойдёт до «граничного» модуля — это последний элемент в цепочке, который явно помечен как принимающий HMR-обновления. Именно этот файл и все его зависимости сервер «перебилдит», а клиент запросит на обновление. В случае, если этим «граничным» модулем окажется корневой модуль app.js, то приложение перезагрузится целиком.

Так как в движке используется механизм нативных ES-модулей найти «граничный» родительский модуль и заменить его — достаточно. Все дочерние зависимости модуля уже обработаются и загрузятся автоматически самим браузером.

Итоговый найденный «граничный» модуль теперь нужно «подменить», а затем перестроить дерево зависимостей (убрать старые связи и добавить новые), ведь во внесённых изменениях могли быть подключены новые модули.

Как же именно «подменяется» модуль со старого на новый?

В случае, если используются нативные ES-модули, выполняется динамический import интересующего модуля. А если преварительно изменившиеся файлы ещё нужно «сбилдить», то перед импортом ещё выполняется этот этап билда.

Чтобы импортировать свежий файл (не кешированную браузером версию), то к импорту можно добавить уникальную метку, например, текущий timestamp:


import(muduleName + `?time=${updateID}`)


Остаётся решить вопрос, как при такой «подмене» передать в новый модуль состояние из старого модуля.

Для этого на колбэке динамического импорта нового модуля нужно забрать интересущие данные из старого модуля и записать данные в новый.

Продолжение 👇
More from @web_platform
  1. Aug 14, 2026Мой воркфлоу с ИИшками сейчас У меня счас несколько подписок: • антропик 20$ (для разработ…
  2. Jul 13, 2026Hermes vs OpenClaw В комметах к прошлому посту @fogrew спросил, чем Hermes отличается от O…
  3. Jul 8, 2026Недавний инцидент с ДЦ сподвиг меня причесать всё на VPS с ботом-дайджестёром. Раньше было…
  4. Jul 2, 2026вчера мой ботик @web_platform_pulse_bot не прислал новости, а всё потому что кукольный теа…
  5. Jun 22, 2026btw там интересная система в репе, используются git submodules: в .gitmodules указывается…
  6. Jun 22, 20264 мая -> 20 июня, столько провисел мой pr в zed до мерджа зато теперь тема superset доступ…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →