← Назад к вопросам

Как взаимосвязаны мемоизация и кэширование?

2.0 Middle🔥 71 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мемоизация и кэширование: разные инструменты, одна цель

Мемоизация и кэширование — разные концепции, но работают в одной упряжке. Оба служат оптимизации через сохранение результатов, но применяются на разных уровнях.

Различия

Мемоизация (Memoization):

  • Уровень: функция или компонент
  • Область: память приложения (RAM)
  • Время жизни: время сессии пользователя
  • Цель: избежать пересчёта функции с одинаковыми параметрами
  • Масштаб: локальная оптимизация
// Мемоизация функции
const fibonacci = (() => {
  const cache = {};
  
  return (n) => {
    if (n in cache) return cache[n];
    if (n < 2) return n;
    
    cache[n] = fibonacci(n - 1) + fibonacci(n - 2);
    return cache[n];
  };
})();

fibonacci(100); // Быстро, результат в cache
fibonacci(100); // Мгновенно, возвращает из cache

Кэширование (Caching):

  • Уровень: любой (функция, API, DOM, браузер, CDN, сервер)
  • Область: памяти разных уровней (RAM, диск, сеть)
  • Время жизни: настраивается (часы, дни, очистка вручную)
  • Цель: избежать дорогостоящих операций (запросы, вычисления)
  • Масштаб: глобальная стратегия
// Кэширование HTTP запроса
const userCache = new Map();

async function getUser(id) {
  if (userCache.has(id)) {
    return userCache.get(id);
  }
  
  const user = await fetch(`/api/users/${id}`).then(r => r.json());
  userCache.set(id, user);
  return user;
}

await getUser(123); // Запрос в сеть
await getUser(123); // Из кэша, мгновенно

Уровни кэширования в веб-приложении

Пользователь
     ↓
1. Браузер кэш (HTTP Cache-Control headers)
     ↓
2. Service Worker (PWA кэш)
     ↓
3. Application Layer (Redux, Zustand, React Query)
     ↓
4. Мемоизация функций (useMemo, useCallback)
     ↓
5. Network (HTTP запрос)
     ↓
6. CDN кэш
     ↓
7. Сервер Application кэш (Redis)
     ↓
8. Database

Примеры в React

1. useMemo — мемоизация вычислений

function Component({ items }) {
  // Дорогое вычисление пересчитывается только если items изменился
  const sorted = useMemo(() => {
    console.log('Сортирую...');
    return [...items].sort((a, b) => a - b);
  }, [items]);

  return <div>{sorted.length}</div>;
}

Это мемоизация — сохраняем результат сортировки.

2. useCallback — мемоизация функции

function Parent() {
  const [items, setItems] = useState([]);

  // Функция мемоизируется, чтобы не пересоздавалась при каждом рендере
  const handleAdd = useCallback((item) => {
    setItems(prev => [...prev, item]);
  }, []);

  // Функция не меняется (по reference) → Child не ререндерится
  return <Child onAdd={handleAdd} />;
}

Это мемоизация функции в памяти.

3. React.memo — мемоизация компонента

const MemoizedList = React.memo(({ items }) => {
  console.log('Рендер List');
  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
});

function Parent({ items }) {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      {/* List не ререндерится, если items не изменился */}
      <MemoizedList items={items} />
    </>
  );
}

Это мемоизация компонента — если props не изменились, старый результат рендера переиспользуется.

4. HTTP кэширование (React Query)

import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  // Кэширование на уровне приложения
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
    staleTime: 5 * 60 * 1000, // 5 минут
  });

  return <div>{user?.name}</div>;
}

// Другой компонент
function UserBio({ userId }) {
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    // Если userId 123 в кэше (свежее), запрос не происходит
  });

  return <p>{user?.bio}</p>;
}

Это кэширование на уровне приложения — данные с сервера хранятся в памяти.

Как они работают вместе

// Полный пример оптимизации

const expensiveCalculation = (arr) => {
  // Дорогая операция: O(n²)
  return arr.reduce((acc, val, i) => {
    for (let j = i; j < arr.length; j++) {
      acc += arr[i] * arr[j];
    }
    return acc;
  }, 0);
};

function DataProcessor({ data }) {
  // Уровень 1: Мемоизация вычисления
  const memoizedResult = useMemo(() => {
    return expensiveCalculation(data);
  }, [data]);

  // Уровень 2: Кэширование результата в localStorage (браузер)
  useEffect(() => {
    localStorage.setItem(
      `cache_${JSON.stringify(data)}`,
      memoizedResult.toString()
    );
  }, [memoizedResult, data]);

  return <div>{memoizedResult}</div>;
}

API кэширование: практический пример

// Свой кэш с мемоизацией
class APICache {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private ttl = 5 * 60 * 1000; // 5 минут

  async fetch(url) {
    const cached = this.cache.get(url);
    const now = Date.now();

    // Кэш ещё свежий
    if (cached && now - cached.timestamp < this.ttl) {
      console.log('Из кэша');
      return cached.data; // Мемоизированный результат
    }

    // Кэш устарел — делаем запрос
    console.log('Запрос в сеть');
    const response = await fetch(url).then(r => r.json());
    
    // Кэшируем результат
    this.cache.set(url, {
      data: response,
      timestamp: now
    });

    return response;
  }
}

const apiCache = new APICache();

await apiCache.fetch('/api/users'); // Сеть
await apiCache.fetch('/api/users'); // Кэш
// Через 5 минут
await apiCache.fetch('/api/users'); // Снова сеть

Проблемы и ограничения

Переполнение памяти (мемоизация):

// Плохо: кэш растёт без конца
const userCache = new Map();

function cacheUser(id, data) {
  userCache.set(id, data); // Может заполнить всю память
}

// Хорошо: ограничиваем размер кэша (LRU cache)
class LRUCache {
  constructor(maxSize = 100) {
    this.maxSize = maxSize;
    this.cache = new Map();
  }

  set(key, value) {
    if (this.cache.size >= this.maxSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }

  get(key) {
    return this.cache.get(key);
  }
}

Инвалидация кэша:

// Когда пользователь редактирует данные, кэш устаревает
async function updateUser(id, data) {
  await fetch(`/api/users/${id}`, { method: 'PUT', body: JSON.stringify(data) });
  
  // Инвалидируем кэш
  userCache.delete(id);
  
  // Или вся история поиска
  userCache.clear();
}

Итог

Мемоизация:

  • Локальная оптимизация
  • Сохраняет результат функции
  • Время жизни: сессия
  • React хуки: useMemo, useCallback, React.memo

Кэширование:

  • Глобальная стратегия
  • Сохраняет данные разных видов (API, DOM, локальное хранилище)
  • Время жизни: настраивается (часы, дни, вечно)
  • Инструменты: localStorage, sessionStorage, HTTP Cache, Redis, React Query

Вместе:

  • Мемоизация = кэширование на уровне функции
  • Кэширование = мемоизация на уровне приложения
  • Используй оба для максимальной производительности
Как взаимосвязаны мемоизация и кэширование? | PrepBro