Кеширование стратегии

Кеширование является важной частью оптимизации веб-приложений, особенно при работе с компонентами и их состояниями. В Preact, как и в React, подход к кешированию ориентирован на минимизацию повторных вычислений, рендеров и сетевых запросов. Основная цель — уменьшить нагрузку на DOM и ускорить обновление интерфейса.

Ключевое отличие Preact от React заключается в компактности ядра и более легковесной системе обновления компонентов, что делает стратегию кеширования особенно значимой при крупных приложениях.


Типы кеширования

  1. Кеширование компонентов Используется для предотвращения повторного рендера компонентов с одинаковыми свойствами. Основной инструмент — memo, который сохраняет результат рендера и возвращает его при неизменных props.

    import { h } from 'preact';
    import { memo } from 'preact/compat';
    
    const ExpensiveComponent = ({ data }) => {
      console.log('Рендер ExpensiveComponent');
      return <div>{data}</div>;
    };
    
    export default memo(ExpensiveComponent);

    Здесь memo сравнивает предыдущие и новые props и рендерит компонент только при их изменении. Можно использовать кастомную функцию сравнения для более сложных случаев.

  2. Кеширование функций и вычислений Для снижения нагрузки на процессор при сложных вычислениях используется хук useMemo.

    import { h } from 'preact';
    import { useMemo } from 'preact/hooks';
    
    const ListComponent = ({ items }) => {
      const sortedItems = useMemo(() => {
        console.log('Сортировка элементов');
        return items.sort((a, b) => a.value - b.value);
      }, [items]);
    
      return <ul>{sortedItems.map(item => <li key={item.id}>{item.value}</li>)}</ul>;
    };

    useMemo гарантирует, что сортировка выполняется только при изменении items.

  3. Кеширование событий и коллбеков Часто полезно сохранять обработчики событий с помощью useCallback, чтобы они не создавались заново при каждом рендере.

    import { h } from 'preact';
    import { useCallback, useState } from 'preact/hooks';
    
    const Counter = () => {
      const [count, setCount] = useState(0);
    
      const increment = useCallback(() => setCount(c => c + 1), []);
    
      return <button onCl ick={increment}>{count}</button>;
    };

    useCallback возвращает одну и ту же функцию между рендерами, что уменьшает количество ненужных обновлений дочерних компонентов.


Кеширование данных

Для приложений с большим количеством асинхронных запросов важно хранить данные в кеше, чтобы минимизировать сетевые вызовы.

  • Локальный кеш в компоненте через useState и useEffect:
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';

const DataFetcher = ({ url }) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    let cancelled = false;
    fetch(url)
      .then(res => res.json())
      .then(json => {
        if (!cancelled) setData(json);
      });
    return () => { cancelled = true; };
  }, [url]);

  return <pre>{JSON.stringify(data, null, 2)}</pre>;
};
  • Глобальный кеш с использованием сторонних библиотек, таких как SWR или React Query (работает с Preact через совместимость с React API), обеспечивает автоматическое обновление и повторное использование данных без лишних запросов.

Стратегии обновления кеша

  1. Stale-While-Revalidate Данные сразу отображаются из кеша, а фоновый запрос обновляет их. Позволяет уменьшить задержку рендера.

  2. Cache-First Сначала проверяется кеш, и только при отсутствии данных выполняется сетевой запрос. Идеально для редко меняющихся данных.

  3. Network-First Сначала делается запрос к серверу, а затем сохраняется результат в кеше. Полезно для критичных к актуальности данных компонентов.


Оптимизация рендера через ключи

Правильное использование атрибута key в списках компонентов напрямую влияет на работу кеша Preact. Необходимо использовать уникальные и стабильные значения для key, чтобы Preact мог повторно использовать существующие DOM-узлы:

const List = ({ items }) => (
  <ul>
    {items.map(item => <li key={item.id}>{item.name}</li>)}
  </ul>
);

Неправильные или меняющиеся ключи приводят к полному удалению и повторному созданию элементов, что снижает эффективность кеширования.


Lazy Loading и кеширование

Компоненты можно загружать по требованию с помощью lazy:

import { h, lazy, Suspense } from 'preact/compat';

const LazyComponent = lazy(() => import('./HeavyComponent'));

const App = () => (
  <Suspense fallback={<div>Загрузка...</div>}>
    <LazyComponent />
  </Suspense>
);

Suspense позволяет показывать резервный контент, пока компонент не загрузится, а последующие рендеры используют уже кешированный модуль.


Итоговые рекомендации

  • Использовать memo для тяжелых компонентов, особенно в больших списках и при частых обновлениях состояния.
  • Применять useMemo и useCallback для дорогостоящих вычислений и коллбеков, чтобы минимизировать лишние рендеры.
  • Организовать кеш данных с учетом частоты обновления и критичности информации, применяя стратегии Cache-First, Network-First или Stale-While-Revalidate.
  • Следить за ключами элементов в списках, чтобы Preact мог эффективно повторно использовать существующие узлы DOM.
  • Использовать lazy loading для тяжелых компонентов и модулей, комбинируя с Suspense для плавного пользовательского опыта.

Эффективное кеширование в Preact сочетает правильное управление состояниями, обработкой вычислений и оптимизацией сетевых данных, что позволяет достигать высокой производительности без избыточного кода и сложных структур.