背景
我目前参与的这个项目有点像是低代码平台,由于是公司产品,我不便透露,我们只用关心我是需要去请求一些模型,这个模型只是针对于界面展示形式的一些信息,不涉及数据,但是需要频繁请求,因此这就需要将请求封装一层,将结果缓存,等下次再次请求的时候直接从缓存中拿。这个方案很好的解决了频繁请求统一接口,而这个接口又不涉及实时性的问题时的性能优化。
但是,这样并不能很好的去解决一种情况:
当我同时请求多次一样的接口时,是来不及缓存的(虽然这样做很奇怪,但是我们确实是有这样的需求的)
开发技术:angular16
原因
同一时间请求接口,他们都是异步的,所以第一个接口发送出去之后拿到返回数据时,其他同时发送的接口已经发送过了,甚至于他们的结果同时返回,那么此时就缓存不上了。
尝试
第一个尝试:请求接口时同步化处理。
这个解决方案可能对一些情况下有用,但针对于我目前来说,前面也提过,我这个项目像是一个低代码平台,所以我写的大多都是基础组件类一样的组件,我之所以同时请求多次接口是因为我同时加载这个小组件,就像是表格的toolbar一样。
所以组件与组件之间是做不到同步请求的。
第二次尝试:即时缓存
这是同事帮我实现的一种方法,我get到这个知识点了!
具体实现逻辑是:借助全局服务,在其中创建一个存储的map或者对象,之后可以使用如下代码
initModel(render: IToolbarRenderParameter): Observable<IToolbarUiModel> {
// 请求前先获取缓存
const obs = this.cachedUiModelMap.get(render['toolbar']!);
if (obs) {
// 如果有就拿取缓存内容
return obs.asObservable();
} else {
// 如果没有就进行缓存,同时正常发送请求
const obs = new BehaviorSubject<IToolbarUiModel>({ name: '' });
this.cachedUiModelMap.set(render['toolbar']!, obs);
// 发送请求
...(代码省略)
return obs;
}
}
封装
上方的方案是针对于组件内部的,那么拓展之后,如果可以在请求拦截器中做一个几秒内的缓存处理。
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of, takeUntil, timer } from 'rxjs';
import { shareReplay, tap } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
// 缓存的map
private cache = new Map();
// 缓存时间
private cacheTime = 5000;
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
const key = request.urlWithParams;
// 可以在请求头里配置,如果有needCache属性为true,就不进行缓存操作,而是直接进行网络请求
const needCache = request.headers.get('needCache');
if (needCache) {
// 去掉这个请求头,不然会报错
request = request.clone({
headers: request.headers.delete('needCache')
});
// 如果没有缓存的数据或者超过限定时长,就正常请求
if (!this.cache.has(key) || Date.now() - this.cache.get(key).lastTime > this.cacheTime) {
const cachTimer$ = timer(this.cacheTime);
const cachRequest$ = next.handle(request).pipe(
takeUntil(cachTimer$),
shareReplay(1),
tap(event => {
this.cache.set(key, {
data: cachRequest$,
lastTime: Date.now()
});
})
);
// 在限定时间结束后,清空下缓存
cachTimer$.subscribe(() => this.cache.delete(key));
return cachRequest$;
}
// 拿取缓存数据
return this.cache.get(key).data;
}
return next.handle(request);
}
}