我想在 Electron 端这样做的主要原因是我希望缓存在应用程序运行之间保持不变。再想一想,我意识到这是完全没有必要的,因为浏览器有多种持久化数据的方式。
我的解决方案使用了 HttpInterceptor 和 CacheService found in this answer 的修改版本,但将数据持久化到 localStorage。
http-interceptor.service.ts:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import { CacheService } from './cache.service';
@Injectable({ providedIn: 'root' })
export class CacheInterceptor implements HttpInterceptor {
constructor(private readonly cacheService: CacheService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
if(req.method !== 'GET') {
return next.handle(req);
}
const cachedResponse: HttpResponse<any> = this.cacheService.get(req);
if (cachedResponse) {
const response = new HttpResponse({ body: cachedResponse });
return of(response);
} else {
return next.handle(req)
.pipe(tap(stateEvent => {
if (stateEvent instanceof HttpResponse) {
this.cacheService.set(req, stateEvent.clone());
}
})
);
}
}
}
cache.service.ts - 为了确保随着缓存大小增加的响应能力,我只在缓存初始化时从 localStorage 加载值,否则只从应该是序列化的副本的map 中读取本地存储中的数据。每次set 调用后仍需要写入 localStorage 以确保事情保持同步。缓存条目每 12 小时过期一次并被删除以保持 localStorage 内存较低。如果您的应用要长时间打开,这可以在很长的setInterval 上完成。
import { HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
const LOCAL_CACHE_KEY = 'localCache';
const STALE_TIME = 1000 * 60 * 60 * 12; // 12 hours
@Injectable()
export class CacheService {
cacheMap: Map<string, any>;
constructor() {
const map = localStorage.getItem(LOCAL_CACHE_KEY);
if (map) {
this.cacheMap = new Map(JSON.parse(map));
} else {
this.cacheMap = new Map(null);
}
this.cacheMap.forEach((value, key) => {
if (Date.now() - value.addedTime > STALE_TIME) {
this.cacheMap.delete(key);
}
});
this.saveCacheMap();
}
get(req: HttpRequest<any>): HttpResponse<any> | undefined {
const cached = this.cacheMap.get(req.urlWithParams);
if (!cached || Date.now() - cached.addedTime > STALE_TIME) {
return undefined;
}
return cached.data;
}
set(req: HttpRequest<any>, response: HttpResponse<any>): void {
if (response.status === 200) {
const entry = { data: response.body, addedTime: Date.now() };
this.cacheMap.set(req.urlWithParams, entry);
this.saveCacheMap();
}
}
private saveCacheMap(): void {
localStorage.setItem(LOCAL_CACHE_KEY, JSON.stringify([...this.cacheMap]));
}
}
链接答案的唯一另一个重大变化是我没有将整个响应存储在地图中,而只是body。从 localStorage 加载时,response.clone() 方法丢失了,因此存储整个响应没有意义,这让我们节省了一点存储空间。不过,这确实需要在 CacheInterceptor 中创建一个新的 HttpResponse。