Мемоизация в библиотеке Haunted предназначена для оптимизации
вычислений внутри функциональных веб-компонентов. Библиотека
предоставляет хук useMemo, позволяющий кешировать результат
вычисления до тех пор, пока набор зависимостей не изменится. При
правильном применении уменьшается количество повторных вычислений и
перерасчётов шаблона, что особенно заметно при работе с массивами
данных, генерацией виртуальных списков и сложной бизнес-логикой.
Хук принимает функцию-вычислитель и массив зависимостей. Вычислитель вызывается только тогда, когда хотя бы одна из зависимостей меняется. Если зависимости остались прежними, возвращается предыдущий результат из кеша.
Ключевой момент: мемоизация не предотвращает повторный рендер компонента, но снижает стоимость вычислений при рендере, когда тяжёлые операции заменяются чтением значения из кеша.
useMemo применим, когда вычисление является
детерминированным и зависит от входных параметров:
import { component, useState, useMemo } from 'haunted';
function Stats() {
const [items, setItems] = useState([]);
const total = useMemo(
() => items.reduce((sum, i) => sum + i.value, 0),
[items]
);
return html`<div>Сумма: ${total}</div>`;
}
customElements.define('app-stats', component(Stats));
В примере reduce выполняется только при изменении
массива items, а не на каждом рендере.
Помимо численных значений мемоизация удобна для формирования производных структур, например, сортированных или отфильтрованных массивов:
const sorted = useMemo(
() => data.slice().sort((a, b) => a.score - b.score),
[data]
);
Без мемоизации сортировка выполнялась бы при каждом обновлении
интерфейса, даже если data не менялась.
Наиболее ощутимый эффект достигается при работе с ресурсоёмкими функциями: подсчёты статистики, подготовка данных для визуализаций, парсинг крупных структур. Подобные операции не должны запускаться без необходимости.
Практическое правило: если вычисление явно тяжелее, чем проверка зависимостей, его стоит мемоизировать.
Частая ошибка заключается в передаче в массив зависимостей новых объектов или функций, создаваемых при каждом рендере. В таком случае мемоизация теряет смысл, потому что зависимость считается изменённой на каждом шаге. Возможные решения:
useCallback для мемоизации колбэков;При работе с виртуальными списками или таблицами мемоизация позволяет стабилизировать вычисления и ускорить перерисовку. Вычисление сглаживания, сегментации или выборки строк применяется только при изменении исходных данных, а не при каждом рендере самого компонента.
const virtualRows = useMemo(
() => buildVirtualRows(data, viewport),
[data, viewport]
);
Хотя useMemo не мемоизирует шаблоны напрямую, через него
можно формировать промежуточные данные: отфильтрованные коллекции,
список свойств, результаты вычислений. Это уменьшает количество диффов,
выполняемых движком рендеринга, так как структура данных остаётся
стабильной.
useMemo и useCallbackОба хука служат для мемоизации, но цели различны.
useMemo кеширует значение, в то время как
useCallback кеширует функцию.
Мемоизировать вычисления следует через useMemo, в то время
как стабилизацию обработчиков событий — через
useCallback.
Практическая рекомендация: если результат вычисления
не является функцией, корректный выбор — useMemo.
Избыточное применение мемоизации усложняет код и увеличивает накладные расходы проверки зависимостей. Эффективно мемоизируются только те операции, которые:
Хук ожидает, что вычислитель является чистой функцией: не производит побочных эффектов и возвращает значение, зависящее только от зависимостей. Нарушение этих условий приводит к сложным для отладки ошибкам и неконсистентному состоянию компонента.
Кеш, создаваемый useMemo, привязан к конкретному
экземпляру компонента, поэтому для каждого экземпляра компонента
создаётся собственный набор кешированных значений. Это важно при
использовании переиспользуемых компонентиев на одной странице.
useMemo органично сочетается с другими хуками Haunted.
Например, вычисление зависимости перед установкой состояния делает
обновления более предсказуемыми:
const filtered = useMemo(() => items.filter(x => x.visible), [items]);
const count = useMemo(() => filtered.length, [filtered]);
Каскад мемоизированных вычислений упрощает отслеживание изменений и снижает нагрузку.
При построении иерархий, деревьев и графов мемоизация предотвращает повторный обход структуры. Особенно полезно, когда итоговая структура используется для визуализации или рендеринга вложенных компонентиев.
Мемоизация в Haunted служит для того, чтобы вычисления происходили только при изменении зависимостей. При корректной настройке достигается баланс между читаемостью кода и производительностью рендеринга функциональных веб-компонентов.