Батчинг обновлений — ключевая оптимизация, которая позволяет Preact минимизировать количество операций с виртуальным DOM и реальным DOM, улучшая производительность приложений. В основе механизма лежит агрегирование нескольких изменений состояния в одну транзакцию, что снижает накладные расходы на перерасчёт и перерисовку компонентов.
Когда состояние компонента изменяется через setState или
хуки (useState), Preact не обновляет DOM немедленно. Вместо
этого изменения помещаются в очередь. После того как текущий стек
вызовов JavaScript завершает выполнение, очередь обрабатывается единым
батчем:
Это снижает количество лишних рендеров и позволяет поддерживать высокую скорость работы интерфейса.
Синхронный батчинг возникает внутри обработчиков событий DOM. Например:
import { h } from 'preact';
import { useState } from 'preact/hooks';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(c => c + 1);
setCount(c => c + 1);
setCount(c => c + 1);
};
return <button onCl ick={increment}>{count}</button>;
}
Несмотря на три вызова setCount, Preact объединяет их в
один рендер. Конечное значение count увеличивается на 3 за
один проход.
Асинхронный батчинг применяется в промисах,
setTimeout, requestAnimationFrame или других
отложенных вызовах:
setTimeout(() => {
setCount(c => c + 1);
setCount(c => c + 1);
}, 0);
В таких случаях каждое изменение состояния может быть обработано
отдельно, так как события происходят в разных стэках вызовов. Для
контроля можно использовать ручной батчинг через функцию
unstable_batchedUpdates (аналогичный React API):
import { unstable_batchedUpdates } from 'preact/compat';
setTimeout(() => {
unstable_batchedUpdates(() => {
setCount(c => c + 1);
setCount(c => c + 1);
});
}, 0);
Батчинг тесно связан с механизмом виртуального DOM и стратегиями diffing. Preact сравнивает предыдущий и новый виртуальный DOM и вычисляет минимальный набор изменений для реального DOM:
При использовании хуков батчинг сохраняет последовательность вызовов:
useState и useReducer собирают обновления
до следующего рендера.useEffect и useLayoutEffect выполняются
после того, как все обновления батча завершены, что гарантирует
согласованность состояния и DOM.import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
const id = setInterval(() => setTime(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <div>{time}</div>;
}
В данном примере состояние time обновляется каждую
секунду, но DOM перерисовывается только после завершения каждой итерации
батча, что предотвращает лишние промежуточные рендеры.
setState, чтобы
гарантировать корректное накопление изменений в батче.unstable_batchedUpdates, если требуется объединение
обновлений.Батчинг обновлений позволяет Preact сохранять лёгкость и высокую производительность, одновременно упрощая управление состоянием и снижая количество ненужных рендеров компонентов. Эффективное использование этого механизма особенно важно в масштабных приложениях с динамическими интерфейсами и высокой частотой событий.