Кеширование HTTP-запросов — ключевой механизм оптимизации клиентских приложений. Оно снижает количество сетевых обращений, ускоряет отображение данных, уменьшает нагрузку на сервер и повышает устойчивость приложения к временным проблемам сети. В Angular кеширование реализуется на уровне сервисов и перехватчиков, что позволяет централизованно управлять логикой хранения и повторного использования ответов.
Angular не предоставляет встроенного автоматического кеширования для
HttpClient, что является осознанным архитектурным решением.
Это даёт разработчику полный контроль над стратегией кеширования и её
соответствием бизнес-требованиям.
Работа с HTTP в Angular строится вокруг следующих компонентов:
Кеширование чаще всего реализуется через
HttpInterceptor, так как он позволяет:
Простейший подход — хранение данных в сервисе:
@Injectable({ providedIn: 'root' })
export class ProductsService {
private cache: Product[] | null = null;
constructor(private http: HttpClient) {}
getProducts(): Observable<Product[]> {
if (this.cache) {
return of(this.cache);
}
return this.http.get<Product[]>('/api/products').pipe(
tap(data => this.cache = data)
);
}
}
Особенности:
Интерцепторы позволяют реализовать универсальный кеш для всех HTTP-запросов.
Обычно используется Map, где ключ — URL запроса, а
значение — HttpResponse.
@Injectable()
export class HttpCache {
private cache = new Map<string, HttpResponse<any>>();
get(url: string): HttpResponse<any> | undefined {
return this.cache.get(url);
}
put(url: string, response: HttpResponse<any>): void {
this.cache.set(url, response);
}
}
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
constructor(private cache: HttpCache) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.method !== 'GET') {
return next.handle(req);
}
const cachedResponse = this.cache.get(req.urlWithParams);
if (cachedResponse) {
return of(cachedResponse.clone());
}
return next.handle(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
this.cache.put(req.urlWithParams, event);
}
})
);
}
}
Ключевые моменты:
GET-запросы;urlWithParams для уникальности;HttpResponse клонируется из-за неизменяемости.@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true }
]
})
export class AppModule {}
Angular применяет интерцепторы в порядке их регистрации.
Без ограничения времени кеш может привести к использованию устаревших данных. Распространённый подход — добавление времени жизни (TTL).
interface CacheEntry {
response: HttpResponse<any>;
timestamp: number;
}
const MAX_AGE = 30000;
get(url: string): HttpResponse<any> | null {
const entry = this.cache.get(url);
if (!entry) return null;
if (Date.now() - entry.timestamp > MAX_AGE) {
this.cache.delete(url);
return null;
}
return entry.response;
}
Такой механизм позволяет автоматически очищать устаревшие данные.
Кеш необходимо сбрасывать при:
Простейшая стратегия — очистка кеша при любом небезопасном запросе:
if (req.method !== 'GET') {
this.cache.clear();
}
Для более точного контроля можно удалять только связанные URL.
HTTP-ответы могут содержать директивы кеширования:
Cache-ControlExpiresETagAngular не обрабатывает их автоматически, но интерцептор может анализировать заголовки:
const cacheControl = event.headers.get('Cache-Control');
if (cacheControl?.includes('no-store')) {
return;
}
ETag позволяет серверу сообщить версию ресурса:
If-None-Match.304 Not Modified.const etag = cachedResponse.headers.get('ETag');
const conditionalReq = req.clone({
headers: req.headers.set('If-None-Match', etag!)
});
Это снижает объём передаваемых данных и ускоряет ответы.
Для сложных запросов одного URL недостаточно. Ключ кеша может включать:
const cacheKey = `${req.urlWithParams}|${JSON.stringify(req.body)}`;
Вместо памяти можно использовать:
localStoragesessionStorageПример сохранения ответа:
localStorage.setItem(key, JSON.stringify(response.body));
Ограничения:
HttpResponse напрямую;На практике применяются гибридные подходы:
Такой подход позволяет добиться баланса между актуальностью данных и производительностью.
Кеширование HTTP-запросов в Angular — это не вспомогательная оптимизация, а полноценная часть архитектуры клиентского приложения, требующая продуманного и контролируемого подхода.