Preact — легковесный фреймворк для построения пользовательских интерфейсов, который обеспечивает высокую производительность благодаря минимальному объёму и оптимизированной виртуальной DOM-структуре. Однако при разработке сложных приложений важно применять дополнительные методы оптимизации рендеринга, чтобы снизить нагрузку на браузер и ускорить обновление интерфейса.
В основе Preact лежит виртуальный DOM (vDOM) — легковесная копия реального DOM. Все изменения сначала происходят в vDOM, после чего Preact сравнивает старую и новую версии виртуального дерева с помощью алгоритма diffing и обновляет только изменённые узлы реального DOM.
Ключевые моменты работы vDOM:
Ререндеринг компонентов происходит при изменении их состояния
(state) или пропсов (props). Даже
незначительное изменение может вызвать перерасчёт всего дерева
подкомпонентов. Для оптимизации применяются следующие подходы:
Мемоизация компонентов Preact.memo
позволяет оборачивать функциональные компоненты, предотвращая их
ререндер, если пропсы не изменились:
import { h } from 'preact';
import { memo } from 'preact/compat';
const ExpensiveComponent = ({ data }) => {
console.log('Rendering ExpensiveComponent');
return <div>{data}</div>;
};
export default memo(ExpensiveComponent);
Мемоизация особенно эффективна для компонентов с тяжёлой логикой или сложными деревьями JSX.
Использование useCallback и
useMemo Для сохранения ссылок на функции и
вычисленные значения между рендерами:
import { h } from 'preact';
import { useMemo, useCallback } from 'preact/hooks';
const List = ({ items, onItemClick }) => {
const sortedItems = useMemo(() => items.sort(), [items]);
const handleClick = useCallback((item) => {
onItemClick(item);
}, [onItemClick]);
return (
<ul>
{sortedItems.map(item => (
<li key={item} onCl ick={() => handleClick(item)}>
{item}
</li>
))}
</ul>
);
};
useMemo предотвращает повторные вычисления, а
useCallback сохраняет функции между рендерами, что полезно
при передаче их в дочерние компоненты.
Ключи (key) в списках Правильное
использование key при рендеринге списков позволяет
алгоритму diffing эффективно обновлять только изменённые элементы:
{items.map(item => <li key={item.id}>{item.name}</li>)}
Без уникальных ключей Preact будет удалять и пересоздавать элементы, что снижает производительность.
Для больших приложений оптимизация загрузки компонентов и библиотек снижает первоначальное время рендера. Preact поддерживает динамический импорт:
import { h, Suspense } from 'preact';
import { lazy } from 'preact/compat';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const App = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
Suspense обеспечивает отображение запасного контента до
полной загрузки компонента, снижая время блокировки интерфейса.
Локальное состояние Часто ререндер происходит из-за глобального состояния, используемого во многих компонентах. Разделение состояния на локальные куски снижает количество обновлений:
const Counter = () => {
const [count, setCount] = useState(0); // локальное состояние
return <button onCl ick={() => setCount(count + 1)}>{count}</button>;
};Контекст (Context) с осторожностью
Обновление значения контекста вызывает ререндер всех дочерних
компонентов, использующих его. Решение — мемоизация значения или
разбивка контекста на несколько независимых:
const ThemeContext = createContext();
const App = () => {
const theme = useMemo(() => ({ color: 'blue' }), []);
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
};Preact предоставляет встроенные инструменты и возможность использования внешних библиотек для анализа рендеринга:
Эти подходы позволяют Preact оставаться быстрым и отзывчивым даже в больших и сложных приложениях.