ant-design-vue/components/_util/cssinjs/theme/ThemeCache.ts

136 lines
3.8 KiB
TypeScript

import type Theme from './Theme';
import type { DerivativeFunc } from './interface';
// ================================== Cache ==================================
type ThemeCacheMap = Map<
DerivativeFunc<any, any>,
{
map?: ThemeCacheMap;
value?: [Theme<any, any>, number];
}
>;
type DerivativeOptions = DerivativeFunc<any, any>[];
export function sameDerivativeOption(left: DerivativeOptions, right: DerivativeOptions) {
if (left.length !== right.length) {
return false;
}
for (let i = 0; i < left.length; i++) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
}
export default class ThemeCache {
public static MAX_CACHE_SIZE = 20;
public static MAX_CACHE_OFFSET = 5;
private readonly cache: ThemeCacheMap;
private keys: DerivativeOptions[];
private cacheCallTimes: number;
constructor() {
this.cache = new Map();
this.keys = [];
this.cacheCallTimes = 0;
}
public size(): number {
return this.keys.length;
}
private internalGet(
derivativeOption: DerivativeOptions,
updateCallTimes = false,
): [Theme<any, any>, number] | undefined {
let cache: ReturnType<ThemeCacheMap['get']> = { map: this.cache };
derivativeOption.forEach(derivative => {
if (!cache) {
cache = undefined;
} else {
cache = cache?.map?.get(derivative);
}
});
if (cache?.value && updateCallTimes) {
cache.value[1] = this.cacheCallTimes++;
}
return cache?.value;
}
public get(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
return this.internalGet(derivativeOption, true)?.[0];
}
public has(derivativeOption: DerivativeOptions): boolean {
return !!this.internalGet(derivativeOption);
}
public set(derivativeOption: DerivativeOptions, value: Theme<any, any>): void {
// New cache
if (!this.has(derivativeOption)) {
if (this.size() + 1 > ThemeCache.MAX_CACHE_SIZE + ThemeCache.MAX_CACHE_OFFSET) {
const [targetKey] = this.keys.reduce<[DerivativeOptions, number]>(
(result, key) => {
const [, callTimes] = result;
if (this.internalGet(key)![1] < callTimes) {
return [key, this.internalGet(key)![1]];
}
return result;
},
[this.keys[0], this.cacheCallTimes],
);
this.delete(targetKey);
}
this.keys.push(derivativeOption);
}
let cache = this.cache;
derivativeOption.forEach((derivative, index) => {
if (index === derivativeOption.length - 1) {
cache.set(derivative, { value: [value, this.cacheCallTimes++] });
} else {
const cacheValue = cache.get(derivative);
if (!cacheValue) {
cache.set(derivative, { map: new Map() });
} else if (!cacheValue.map) {
cacheValue.map = new Map();
}
cache = cache.get(derivative)!.map!;
}
});
}
private deleteByPath(
currentCache: ThemeCacheMap,
derivatives: DerivativeFunc<any, any>[],
): Theme<any, any> | undefined {
const cache = currentCache.get(derivatives[0])!;
if (derivatives.length === 1) {
if (!cache.map) {
currentCache.delete(derivatives[0]);
} else {
currentCache.set(derivatives[0], { map: cache.map });
}
return cache.value?.[0];
}
const result = this.deleteByPath(cache.map!, derivatives.slice(1));
if ((!cache.map || cache.map.size === 0) && !cache.value) {
currentCache.delete(derivatives[0]);
}
return result;
}
public delete(derivativeOption: DerivativeOptions): Theme<any, any> | undefined {
// If cache exists
if (this.has(derivativeOption)) {
this.keys = this.keys.filter(item => !sameDerivativeOption(item, derivativeOption));
return this.deleteByPath(this.cache, derivativeOption);
}
return undefined;
}
}