Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое закэшировать?
Определение кэширования
Кэширование - это способ сохранить результат вычисления или запроса, чтобы при следующем обращении к одним и тем же данным вернуть сохранённый результат вместо повторного вычисления или запроса. Это ускоряет работу приложения и снижает нагрузку на ресурсы.
Принцип работы
Первый запрос:
Запрос -> Вычисление/Загрузка -> Сохранение в кэш -> Возврат результата
Следующий запрос (с кэшем):
Запрос -> Поиск в кэше -> Найдено! -> Возврат из кэша (быстро)
Следующий запрос (кэш устарел):
Запрос -> Поиск в кэше -> Не найдено/Устарело -> Вычисление -> Обновление кэша
Виды кэширования в Frontend
1. Кэширование значений в памяти (useMemo)
Сохраняешь результат дорогого вычисления, чтобы не пересчитывать его при каждом рендере:
function Component({ items }) {
// БЕЗ кэширования - calculateTotal вызывается при каждом рендере
const total = calculateTotal(items); // Медленно!
// С кэшированием - вычисляется только если items изменился
const total = useMemo(() => calculateTotal(items), [items]);
return <div>Итого: {total}</div>;
}
function calculateTotal(items) {
console.log("Вычисляю...");
return items.reduce((sum, item) => sum + item.price, 0);
}
// При первом рендере: "Вычисляю..." -> результат в кэше
// При втором рендере (items не изменился): результат из кэша, нет console.log
// При третьем рендере (items изменился): "Вычисляю..." -> новый результат
2. Кэширование функций (useCallback)
Сохраняешь функцию, чтобы не создавать новую при каждом рендере:
function Parent() {
const [count, setCount] = useState(0);
// БЕЗ кэширования - новая функция при каждом рендере
const handleClick = () => setCount(count + 1);
// С кэшированием - одна функция (пока dependencies не изменились)
const handleClick = useCallback(() => setCount(c => c + 1), []);
return <Child onClick={handleClick} />;
}
// Child компонент с React.memo
const Child = React.memo(({ onClick }) => {
console.log("Рендер Child");
return <button onClick={onClick}>Нажми</button>;
});
// БЕЗ useCallback: "Рендер Child" при каждом клике (новая функция = новые props)
// С useCallback: "Рендер Child" только один раз (функция та же)
3. Кэширование HTTP-запросов
Сохраняешь результат сетевого запроса, чтобы не повторять запрос к серверу:
// Без кэширования - при каждом обращении идёт запрос на сервер
function useUser(id) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${id}`)
.then(r => r.json())
.then(setUser);
}, [id]);
return user;
}
// Медленно, если один user загружается несколько раз!
// С кэшированием - используешь Map или библиотеку
const userCache = new Map();
function useUser(id) {
const [user, setUser] = useState(null);
useEffect(() => {
// Проверяем кэш
if (userCache.has(id)) {
setUser(userCache.get(id));
return;
}
fetch(`/api/users/${id}`)
.then(r => r.json())
.then(data => {
userCache.set(id, data); // Сохраняем в кэш
setUser(data);
});
}, [id]);
return user;
}
// Первый запрос: fetch
// Второй запрос (с тем же id): из кэша (быстро!)
4. LocalStorage и SessionStorage
Кэширование на уровне браузера - данные сохраняются между сеансами:
function useLocalCache(key, fetcher) {
const [data, setData] = useState(() => {
// При загрузке компонента проверяем localStorage
const cached = localStorage.getItem(key);
return cached ? JSON.parse(cached) : null;
});
useEffect(() => {
if (!data) {
fetcher().then(result => {
setData(result);
localStorage.setItem(key, JSON.stringify(result)); // Сохраняем
});
}
}, []);
return data;
}
// Использование
function Profile() {
const user = useLocalCache("user", () => fetch("/api/me").then(r => r.json()));
return <div>{user?.name}</div>;
}
// При первом посещении: fetch + сохранение в localStorage
// При следующем посещении: данные сразу из localStorage
Примеры кэширования в React
Паттерн с тройным кэшем:
function useFetchWithCache(url) {
const [data, setData] = useState(() => {
// 1. Проверяем localStorage (при загрузке)
const stored = localStorage.getItem(url);
return stored ? JSON.parse(stored) : null;
});
useEffect(() => {
if (data) return; // 2. Если данные есть - ничего не делаем
// 3. Если нет - загружаем
fetch(url)
.then(r => r.json())
.then(result => {
setData(result);
localStorage.setItem(url, JSON.stringify(result));
});
}, [url, data]);
return data;
}
Кэширование на уровне браузера
Browser Cache (через заголовки HTTP):
Сервер отправляет:
Cache-Control: max-age=3600
Браузер:
- Сохраняет файл
- В течение часа не делает новый запрос
- Показывает кэшированную версию
Service Worker - оффлайн кэширование:
// В Service Worker
self.addEventListener(fetch, (event) => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Если в кэше - вернуть из кэша
if (response) return response;
// Иначе - сделать запрос
return fetch(event.request);
})
);
});
// Результат: app работает оффлайн, если данные были закэшированы
Когда кэшировать
Кэшируй:
- Дорогие вычисления (сортировка, фильтрация больших массивов)
- HTTP-запросы (особенно если один запрос делается несколько раз)
- Функции, которые передаёшь в качестве props (для React.memo)
- Результаты парсинга/трансформации данных
Не кэшируй:
- Простые вычисления (сложение, конкатенация строк)
- Часто меняющиеся данные (реал-тайм)
- Большие объекты в памяти (можешь исчерпать RAM)
- Потенциально устаревшие данные без механизма инвалидации
Проблемы с кэшем
Инвалидация кэша - одна из сложнейших задач:
// Проблема: кэш может содержать устаревшие данные
const cache = new Map();
function updateUser(id, data) {
fetch(`/api/users/${id}`, { method: "PUT", body: JSON.stringify(data) })
.then(() => {
// ЗАБЫЛИ инвалидировать кэш!
// cache.delete(id);
});
}
function getUser(id) {
if (cache.has(id)) {
return cache.get(id); // Вернём старые данные!
}
}
Заключение
Кэширование - это переиспользование уже вычисленного результата вместо повторного вычисления. В Frontend это критично для:
- Производительности: быстрее (из кэша vs новое вычисление)
- Экономии ресурсов: меньше запросов к серверу
- Лучшего UX: мгновенная загрузка знакомых данных
Основное правило: кэшируй дорогое, инвалидируй своевременно.