В Preact, как и в React, каждый компонент при изменении состояния
(state) или свойств (props) инициирует процесс
рендеринга, который включает вызов функции компонента и
последующее обновление виртуального DOM. Даже небольшие изменения могут
привести к множественным перерендерингам дочерних компонентов, что
негативно сказывается на производительности, особенно в сложных
интерфейсах с большим количеством элементов.
Ключевой момент: рендеринг — это не всегда обновление реального DOM. Preact использует виртуальный DOM, чтобы минимизировать прямые манипуляции с DOM, но вычислительные затраты на создание виртуальных деревьев остаются.
Для предотвращения лишних перерендерингов используется
мемоизация компонентов. Preact предоставляет функцию
memo, аналогичную React.memo, которая позволяет обернуть
функциональный компонент и ререндерить его только при изменении
переданных props.
import { h, memo } from 'preact';
const Button = memo(({ label, onClick }) => {
console.log('Button render');
return <button onCl ick={onClick}>{label}</button>;
});
Особенности использования:
memo сравнивает props по ссылке. Если
передается новый объект при каждом рендере, мемоизация не
сработает.const Button = memo(
({ label, config }) => <button>{label}</button>,
(prevProps, nextProps) => prevProps.label === nextProps.label && prevProps.config.id === nextProps.config.id
);
useCallbackФункция useCallback позволяет мемоизировать обработчики
событий, чтобы они не создавались заново при каждом рендере. Это
критично для компонентов, обернутых в memo.
import { useState, useCallback } from 'preact/hooks';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <Button label={count} onCl ick={increment} />;
};
useMemouseMemo используется для мемоизации вычисляемых
значений, которые требуют затратных операций.
const expensiveCalculation = useMemo(() => {
return heavyComputation(data);
}, [data]);
Без useMemo каждая перерисовка компонента заново
вычисляла бы heavyComputation, что увеличивает нагрузку на
CPU.
Разделение интерфейса на мелкие компоненты снижает количество
перерендерингов глобальных частей страницы. Если родительский компонент
обновляется, дочерние компоненты, обернутые в memo, не
будут перерендериваться без изменения их props.
const Parent = ({ data }) => {
return (
<div>
<Header title={data.title} />
<Content items={data.items} />
</div>
);
};
const Header = memo(({ title }) => <h1>{title}</h1>);
const Content = memo(({ items }) => <ul>{items.map(item => <li>{item}</li>)}</ul>);
При работе с динамическими списками важно использовать корректные
ключи (key). Preact использует ключи для минимизации
изменений DOM.
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
Правила выбора ключей:
Для длинных списков и таблиц используется
виртуализация, когда рендерятся только элементы,
видимые на экране. Это значительно снижает нагрузку на рендер и повышает
отзывчивость интерфейса. В Preact можно использовать сторонние
библиотеки, совместимые с его API, например
preact-virtual-list.
import VirtualList from 'preact-virtual-list';
<VirtualList
data={items}
rowHeight={50}
rowRenderer={(item) => <div>{item.name}</div>}
/>
<Component options={[1,2,3]} /> // Создаётся новый массив при каждом рендере
Решение: мемоизация через useMemo или вынесение констант
вне компонента.
props как анонимных функций или объектов.useCallback и useMemo для
обработки функций и вычислений.preact/debug и профайлера браузера.Эти подходы в совокупности позволяют добиться высокой отзывчивости интерфейса даже при работе с большими и динамическими приложениями на Preact.