Caching is an essential technique to improve the performance of an Angular application by reducing the load time of frequently accessed resources. There are several strategies that can be used to implement advanced caching and cache invalidation in Angular applications. Here are some of the commonly used strategies:
**1. HTTP Caching using interceptors**
Interceptors play a crucial role in global HTTP request and response handling. We can use interceptors for caching by inspecting if the requested resource is cached or not. If cached, return the cached response, otherwise, fetch it from the server.
For this, you can create a custom interceptor:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
private cache: Map<string, HttpResponse<any>> = new Map();
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.method !== 'GET') {
return next.handle(req);
}
const cachedResponse = this.cache.get(req.url);
if (cachedResponse) {
return of(cachedResponse.clone());
}
return next.handle(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
this.cache.set(req.url, event.clone());
}
})
);
}
}
In the ‘intercep‘ function, we check if the method is not ‘GET‘, then do not cache. If it is a ‘GET‘ request, we serve the response from cache (if available) or fetch it and cache it.
Then, add the interceptor in the ‘providers‘ array in the ‘AppModule‘:
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true },
],
**2. Cache invalidation using key-based strategy**
To invalidate or delete the cache when required, you can use a key-based strategy. The idea is to use a unique key for requests that can be compared and updated with the new changes. You can extend the CacheInterceptor to add cache invalidation controls.
For example, let’s say you want to invalidate the cache when the user explicitly updates any resource. You can create a custom event that will emit when the user performs save action, and you can subscribe to this event in your interceptor to clear the requested resource cache.
Here’s an example of how to invalidate the cache on a specific update event:
1. Create a CacheInvalidationService:
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CacheInvalidationService {
invalidate$: Subject<string> = new Subject();
invalidateKey(key: string) {
this.invalidate$.next(key);
}
}
2. Modify the CacheInterceptor to consume this service:
constructor(private cacheInvalidationService: CacheInvalidationService) {
cacheInvalidationService.invalidate$.subscribe(key => {
if (this.cache.has(key)) {
this.cache.delete(key);
}
});
}
3. Call the ‘invalidateKey‘ method from the service whenever a specific update event occurs:
this.cacheInvalidationService.invalidateKey(url);
**3. Use third-party libraries**
There are several third-party libraries available that can simplify the implementation of advanced caching in Angular applications. Some popular libraries include:
- ngx-cacheable: https://www.npmjs.com/package/ngx-cacheable
- caching-fetch: https://www.npmjs.com/package/caching-fetch
**4. Cache with Service Workers**
Service Workers provide an advanced caching mechanism that allows your Angular applications to cache resources and operate offline. Angular’s ‘@angular/service-worker‘ package, introduced in Angular v5, helps in implementing Service Worker caching with minimal configuration in the ‘ngsw-config.json‘ configuration file.
To use Service Workers, install the package and update the ‘angular.json‘ and ‘app.module.ts‘ files.
ng add @angular/pwa
In the ‘ngsw-config.json‘ file, you can define caching configurations:
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/index.html",
"/favicon.ico",
"/*.css",
"/*.js"
]
}
},
{
"name": "api",
"installMode": "lazy",
"updateMode": "prefetch",
"maxSizeBytes": "1048576",
"timeoutMs": "10000",
"strategy": "performance",
"resources": {
"urls": [
"https://api.example.com/**"
]
}
}
]
In this example, there are two caching strategies:
1. The ‘app‘ group prefetches the static assets of the application.
2. The ‘api‘ group caches the responses coming from the API endpoint ’https://api.example.com’.
To invalidate the cache, you can use the ’maxSizeBytes’ and the ’timeoutMs’ properties. Here, ’maxSizeBytes’ is the maximum size of the cache in bytes, and ’timeoutMs’ is the maximum lifetime of the cache.
The strategies listed above can be combined and customized based on the requirements and desired level of complexity. A well-implemented caching technique can significantly improve Angular applications’ performance and user experience.