Як налагоджувати застосунок та знаходити витоки пам'яті
Загальний процес налагодження
Визначення проблеми
- Додаток зависає, сповільнюється або споживає занадто багато пам'яті?
- Використовуйте інструменти браузера:
DevTools(Performance, Memory),Console,React Profiler.2
Логування та аналіз помилок
- Додайте
console.log,console.trace,console.errorдля відстеження. - Використовуйте зовнішні інструменти: Sentry, LogRocket, Datadog для логування помилок.
Профілювання продуктивності
- Вкладка
Performanceу Chrome DevTools — аналізуйте FPS, час завантаження та рендерингу. React Profiler— визначте непотрібні повторні рендери.Lighthouse— рекомендації щодо оптимізації.
Аналіз витоків пам'яті
- Використовуйте вкладку
Memoryта Heap Snapshot. - Шукайте об'єкти які не видаляються після демонтажу компонент.
- Слідкуйте за
setInterval,setTimeout,WebSocket,EventListeners.
Причини витоків пам'яті та рішення
Забуті таймери та інтервали
useEffect(() => {
const interval = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(interval); // потрібно очистити!
}, []);Невидалені обробники подій
useEffect(() => {
const handleScroll = () => console.log("scrolling...");
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);Замикання, що утримують дані
Замикання можуть "пам'ятати" посилання на об'єкти, і вони не будуть видалені. Рішення: використовуйте
useRef,useCallbackабо очищайте вчасно.
Глобальні змінні
window.myCache = largeObject; // погана практика
// краще явно очистити
window.myCache = null;Посилання в useRef та DOM
const ref = useRef(null);
useEffect(() => {
ref.current = document.getElementById("my-element");
return () => {
ref.current = null; // звільнити посилання
};
}, []);WebSocket або підписки не закриті вчасно
useEffect(() => {
const socket = new WebSocket("wss://example.com");
return () => socket.close(); // потрібно закрити
}, []);Відсутність віртуалізації на великих списках
Рендеринг 1000+ елементів без віртуалізації сповільнює додаток і "забиває" пам'ять.
Рішення: використовуйте бібліотеки:
Як знайти витоки пам'яті
Вкладка Memory → Heap Snapshot
- Перейдіть до DevTools → Memory → Take Snapshot
- Взаємодійте з компонентом (наприклад, відкрийте/закрийте модальне вікно)
- Зробіть ще один Snapshot
- Порівняйте: тимчасові об'єкти повинні зникнути
Вкладка Performance → Запис алокацій
- Натисніть "Start recording allocations"
- Використовуйте компонент
- Натисніть "Stop" і шукайте витоки, які залишилися після видалення компонент
Інструменти моніторингу
- Chrome DevTools — вбудований аналізатор пам'яті та профайлер
- Sentry, Datadog, LogRocket — виявлення та логування витоків і помилок у продакшені
- why-did-you-render — допомагає знайти непотрібні повторні рендери компонентів
Важливо: Витоки пам'яті особливо небезпечні в SPA (Single Page Application), оскільки додаток живе довго. Навіть невеликі витоки можуть призвести до зниження продуктивності з часом.
Висновок
- Слідкуйте за очищенням побічних ефектів (
setInterval,event listeners,sockets) - Використовуйте DevTools → Memory → Snapshot для діагностики
- Не зберігайте глобальні об'єкти або замикання, які не очищуються
- Для великих списків — застосовуйте віртуалізацію
- Використовуйте React Profiler та профілювання браузера для виявлення вузьких місць
// Приклад безпечного патерну useEffect
useEffect(() => {
const res = startSomething();
return () => {
cleanupSomething(res);
};
}, []);Зміст
Загальний процес налагодженняПричини витоків пам'яті та рішенняЗабуті таймери та інтервалиНевидалені обробники подійЗамикання, що утримують даніГлобальні змінніПосилання в useRef та DOMWebSocket або підписки не закриті вчасноВідсутність віртуалізації на великих спискахЯк знайти витоки пам'ятіВкладка Memory → Heap SnapshotВкладка Performance → Запис алокаційІнструменти моніторингуВисновок
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.