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

Какие знаешь способы кеширования данных в Angular?

1.8 Middle🔥 111 комментариев
#Soft Skills и рабочие процессы

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Способы кеширования данных в Angular

В Angular существует несколько подходов к кешированию данных, каждый из которых решает определенные задачи и подходит для разных сценариев. Кеширование — это критически важная техника для оптимизации производительности, уменьшения количества HTTP-запросов и улучшения пользовательского опыта.

1. Сервисы с внутренним кешем (In-Memory Caching)

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

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { shareReplay, tap } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class DataService {
  private cache = new Map<string, any>();
  private cacheTime = new Map<string, number>();
  private readonly CACHE_DURATION = 5 * 60 * 1000; // 5 минут

  constructor(private http: HttpClient) {}

  getProducts(): Observable<Product[]> {
    const cacheKey = 'products';
    const cachedData = this.cache.get(cacheKey);
    const cachedTime = this.cacheTime.get(cacheKey);
    
    // Проверка актуальности кеша
    if (cachedData && cachedTime && 
        (Date.now() - cachedTime) < this.CACHE_DURATION) {
      return of(cachedData);
    }
    
    return this.http.get<Product[]>('/api/products').pipe(
      tap(data => {
        this.cache.set(cacheKey, data);
        this.cacheTime.set(cacheKey, Date.now());
      }),
      shareReplay(1) // Дополнительное кеширование для поздних подписчиков
    );
  }
  
  invalidateCache(key?: string): void {
    if (key) {
      this.cache.delete(key);
      this.cacheTime.delete(key);
    } else {
      this.cache.clear();
      this.cacheTime.clear();
    }
  }
}

2. RxJS операторы для кеширования

RxJS предоставляет мощные операторы для реализации кеширования в реактивном стиле:

  • shareReplay() — кеширует последнее значение и отдает его новым подписчикам
  • publishReplay() + refCount() — более гибкая альтернатива shareReplay()
  • ReplaySubject — специальный тип Subject, который хранит историю значений
import { ReplaySubject, Observable } from 'rxjs';
import { take } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class UserService {
  private usersCache$: ReplaySubject<User[]>;
  
  constructor(private http: HttpClient) {
    this.usersCache$ = new ReplaySubject<User[]>(1);
  }
  
  loadUsers(): void {
    this.http.get<User[]>('/api/users').pipe(
      take(1)
    ).subscribe(users => {
      this.usersCache$.next(users);
    });
  }
  
  getUsers(): Observable<User[]> {
    // Если кеш пуст — загружаем данные
    if (!this.usersCache$.observers.length) {
      this.loadUsers();
    }
    return this.usersCache$.asObservable();
  }
}

3. HTTP Interceptors для автоматического кеширования

HTTP Interceptor позволяет реализовать кеширование на уровне всех HTTP-запросов:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, 
         HttpResponse, HttpEvent } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class CacheInterceptor implements HttpInterceptor {
  private cache = new Map<string, { 
    data: any; 
    timestamp: number 
  }>();
  private readonly MAX_CACHE_AGE = 300000; // 5 минут

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Кешируем только GET-запросы
    if (req.method !== 'GET') {
      return next.handle(req);
    }
    
    const cachedResponse = this.cache.get(req.urlWithParams);
    
    // Проверяем актуальность кеша
    if (cachedResponse && 
        (Date.now() - cachedResponse.timestamp) < this.MAX_CACHE_AGE) {
      return of(new HttpResponse({ 
        body: cachedResponse.data,
        status: 200 
      }));
    }
    
    return next.handle(req).pipe(
      tap(event => {
        if (event instanceof HttpResponse) {
          this.cache.set(req.urlWithParams, {
            data: event.body,
            timestamp: Date.now()
          });
        }
      })
    );
  }
}

4. Локальное хранилище браузера

Для долговременного кеширования между сессиями можно использовать:

  • localStorage — сохраняет данные бессрочно
  • sessionStorage — сохраняет данные только на время сессии
  • IndexedDB — для больших объемов структурированных данных
@Injectable({ providedIn: 'root' })
export class PersistentCacheService {
  private readonly CACHE_PREFIX = 'app_cache_';
  
  set(key: string, data: any, ttl?: number): void {
    const cacheItem = {
      data,
      expiry: ttl ? Date.now() + ttl : null
    };
    localStorage.setItem(
      `${this.CACHE_PREFIX}${key}`, 
      JSON.stringify(cacheItem)
    );
  }
  
  get(key: string): any {
    const item = localStorage.getItem(`${this.CACHE_PREFIX}${key}`);
    if (!item) return null;
    
    const cacheItem = JSON.parse(item);
    
    // Проверка срока действия
    if (cacheItem.expiry && Date.now() > cacheItem.expiry) {
      this.remove(key);
      return null;
    }
    
    return cacheItem.data;
  }
  
  remove(key: string): void {
    localStorage.removeItem(`${this.CACHE_PREFIX}${key}`);
  }
}

5. Специализированные библиотеки

Для сложных сценариев можно использовать специализированные решения:

  • Angular Service Worker (@angular/pwa) — для кеширования API-запросов и статических ресурсов
  • NGX-Cache — библиотека с расширенными возможностями кеширования
  • Akita / NgRx — управление состоянием с встроенными механизмами кеширования

6. Стратегии инвалидации кеша

Инвалидация (сброс) кеша — не менее важная часть, чем само кеширование:

  • TTL (Time To Live) — автоматическое удаление по истечении времени
  • Ручная инвалидация — при изменении данных через POST/PUT/DELETE
  • Зависимые ключи — сброс связанных данных при изменении основного ресурса
  • Версионирование — добавление версии к ключу кеша
@Injectable({ providedIn: 'root' })
export class CacheManagerService {
  private dependencies = new Map<string, string[]>();
  
  addDependency(parentKey: string, childKey: string): void {
    if (!this.dependencies.has(parentKey)) {
      this.dependencies.set(parentKey, []);
    }
    this.dependencies.get(parentKey)!.push(childKey);
  }
  
  invalidateWithDependencies(key: string): void {
    // Удаляем основной ключ
    this.cache.delete(key);
    
    // Удаляем зависимые ключи
    const deps = this.dependencies.get(key);
    if (deps) {
      deps.forEach(depKey => this.cache.delete(depKey));
    }
  }
}

Критерии выбора стратегии кеширования

При выборе подхода учитывайте:

  1. Время жизни данных — как часто они изменяются
  2. Объем данных — сколько памяти готовы выделить
  3. Критичность актуальности — можно ли показывать устаревшие данные
  4. Архитектура приложения — используются ли state-менеджеры
  5. Тип данных — статические справочники vs динамический контент

Оптимальный подход — комбинирование нескольких методов: in-memory кеш для часто используемых данных, HTTP Interceptor для повторяющихся запросов и localStorage для пользовательских настроек или редко изменяющихся справочников. Главное — реализовать четкую стратегию инвалидации, чтобы пользователь всегда видел актуальные данные, когда это необходимо.

Какие знаешь способы кеширования данных в Angular? | PrepBro