Hot Reload — це технологія, за якої зміни в початковому коді застосовуються до працюючого застосунку без повного перезавантаження сторінки та без втрати поточного стану. На відміну від Live Reload, який просто оновлює сторінку повністю, гаряче перезавантаження замінює лише змінені модулі на льоту. За даними Webpack, 2024, Hot Module Replacement (HMR) скорочує час циклу розробки до 80% за рахунок збереження стану компонентів.
Головне
Hot Reload — це механізм розробки, при якому зміни у вихідних файлах негайно відображаються у працюючому застосунку без його перезапуску. Розробник редагує код в редакторі, зберігає файл, і результат одразу видно в браузері або емуляторі.
Ключова властивість технології — збереження стану застосунку. Змінні в пам'яті, дані у формах, поточна навігація — все залишається на місці. Розробнику не потрібно повторювати послідовність дій для повернення до потрібного екрана.
У веб-розробці Hot Reload реалізується через механізм Hot Module Replacement (HMR). Збірник відстежує зміни файлів, визначає, які модулі зачеплені, та надсилає оновлений код у браузер через WebSocket. Браузер замінює лише ці модулі без перезавантаження сторінки.
Популярність Hot Reload злетіла з поширенням односторінкових застосунків (SPA), де повне перезавантаження сторінки знищує весь стан клієнтського застосунку. React, Vue, Angular та інші фреймворки рекомендують HMR як основний режим розробки.
Механізм HMR складається з чотирьох етапів. Збірник на стороні сервера компілює змінений модуль, формує JSON-патч з новою версією коду та надсилає його в браузер через WebSocket-з'єднання. Браузерна частина HMR runtime приймає патч, замінює старий модуль новим та повідомляє підписників модуля.
Webpack Dev Server використовує WebSocket для зв'язку з клієнтом. При зміні файлу збірник формує hot-update.js та hot-update.json з маніфестом змін. Клієнт автоматично завантажує ці файли через JSONP.
// webpack.config.js - налаштування HMR
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devServer: {
hot: true,
liveReload: false
},
plugins: [new HtmlWebpackPlugin()]
};
HMR runtime у браузері перевіряє, чи підтримує замінюваний модуль accept. Якщо модуль оголосив module.hot.accept(), runtime замінює його на місці та запускає колбек оновлення. Якщо accept не оголошено, HMR падає до рівня вище, до батьківського модуля.
У React Fast Refresh ця логіка вбудована на рівні фреймворку — не потрібно вручну писати module.hot.accept у кожному файлі. Babel-плагін react-refresh/babel додає код, який безпечно замінює компоненти без втрати стану.
Ці три терміни часто плутають, але вони кардинально різняться за поведінкою. Live Reload перезавантажує всю сторінку цілком при будь-якій зміні — весь стан втрачається. Full Reload відбувається при повному перезапуску сервера розробки з втратою не лише клієнтського, але й серверного стану.
| Тип | Перезавантаження сторінки | Збереження стану | Швидкість |
|---|---|---|---|
| Hot Reload (HMR) | Ні | Так | Миттєво |
| Live Reload | Так | Ні | 1-3 сек |
| Full Reload | Так | Ні | 3-10 сек |
Вибір між Hot та Live безпосередньо впливає на швидкість розробки. При HMR цикл «змінив код → побачив результат» займає 50-200 мс. При Live Reload — 1-3 секунди плюс час на відновлення контексту. Протягом робочого дня різниця становить десятки хвилин зекономленого часу.
React Fast Refresh — офіційна реалізація Hot Reload, оптимізована для React-компонентів. Вона замінила старий механізм React Hot Loader, який був нестабільним і вимагав ручного налаштування. Fast Refresh вбудований у Create React App, Next.js, Vite та Webpack через react-refresh/babel.
Fast Refresh коректно зберігає стан функціональних компонентів при зміні їх коду. Якщо ви змінюєте лише JSX-розмітку — useState та інші хуки зберігають свої значення. Якщо змінюється логіка хука — відбувається повторне монтування компонента.
// Приклад - стан зберігається при зміні JSX
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
Fast Refresh не зберігає стан, якщо новий код містить помилку компіляції. Якщо компонент експортується не за замовчуванням (named export), стан також може не зберегтися. Рекомендується використовувати default export для компонентів і обгортати експорти в memo, якщо потрібно гарантувати збереження стану.
Сучасні збірники надають HMR. Webpack — піонер HMR з гнучкою конфігурацією через devServer.hot. Vite використовує нативний ESM та esbuild для миттєвого HMR без бандлінгу. Parcel — zero-config HMR. Turbopack — новий збірник від Vercel з HMR на Rust.
| Інструмент | Тип HMR | Конфігурація | Екосистема |
|---|---|---|---|
| Webpack 5 | HMR через WebSocket | devServer.hot: true | React, Vue, Angular |
| Vite | Native ESM HMR | З коробки | React, Vue, Svelte |
| Parcel 2 | Zero-config HMR | Не потрібна | React, Vue, TS |
| Turbopack | Incremental HMR | З коробки | Next.js |
Vite виділяється швидкістю — HMR у Vite працює через нативні ES-модулі браузера. При зміні файлу Vite надсилає лише цей модуль через import(), без перекомпіляції всього проекту. Webpack компілює кожен модуль цілком, що при великих проектах сповільнює HMR.
Розглянемо налаштування HMR для різних збірників. У Webpack потрібно вказати hot: true у devServer та додати react-refresh/babel. У Vite HMR працює з коробки з будь-яким пресетом. У Next.js Fast Refresh увімкнено за замовчуванням через турбокомпілятор.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
hmr: {
port: 3001
}
}
});
// webpack.config.js
const ReactRefreshWebpackPlugin =
require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
devServer: { hot: true },
plugins: [new ReactRefreshWebpackPlugin()],
module: {
rules: [
{
test: /\.jsx?$/u,
use: 'babel-loader'
}
]
}
};
HMR не працює коректно у всіх сценаріях. CSS-модулі з динамічними класами можуть не застосуватися. Глобальні змінні та синглтони (Redux store, роутер) можуть скинутися при перезавантаженні. Важкі бібліотеки при HMR іноді генерують витоки пам'яті через заміну модулів без GC.
Вирішення проблем HMR — ізолювати гарячий код у чисті компоненти без побічних ефектів. Побічні ефекти (підписки на WebSocket, таймери) повинні очищатися в useEffect. Для Redux рекомендується persister, що зберігає стан у sessionStorage. CSS-рішення на кшталт CSS Modules або styled-components коректно працюють з HMR без додаткових налаштувань.
Ще одне обмеження — налагодження в production: HMR навмисно вимикається в production-збірці. Вихідний код мініфіковано, і гаряча заміна на продакшені неможлива. Для налагодження помилок на продакшені використовують source maps та логування, але не HMR.
Часті запитання
Hot Reload замінює лише змінений модуль без перезавантаження сторінки та зберігає стан. Live Reload перезавантажує всю сторінку цілком — стан втрачається, застосунок запускається заново з початкового екрана.
У проектах на Create React App та Vite HMR увімкнено за замовчуванням. У Webpack потрібно додати devServer: { hot: true } та @pmmmwh/react-refresh-webpack-plugin. У Next.js Fast Refresh працює без налаштування.
Причина — помилка компіляції в новому коді, named export замість default, зміна сигнатури хука або мутація стейту напряму. React Fast Refresh безпечно скидає компонент лише тоді, коли це необхідно для коректної роботи.
Так, усі сучасні збірники підтримують HMR з TypeScript. Vite використовує esbuild для транспіляції TS. Webpack — ts-loader або babel з @babel/preset-typescript. Fast Refresh коректно працює з типізованими компонентами.
Перевірте консоль браузера на помилки WebSocket-з'єднання. Переконайтеся, що devServer.hot: true увімкнено. Для Vite перевірте порт HMR. Якщо проблема не вирішується — виконайте повне перезавантаження сторінки (F5) та перевірте консоль збірника.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також