Skip to content

Commit

Permalink
feat: 浏览器缓存Storage支持prefix配置
Browse files Browse the repository at this point in the history
  • Loading branch information
caijf committed Sep 9, 2024
1 parent 25cbf58 commit 185b6a5
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 22 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ local.del('foo');
local.get('foo'); // undefined
```
### Storage 配置
```typescript
export type StorageOptions = {
needParsed: boolean; // 存取数据时是否需要解析和序列化数据。如果使用内存缓存,默认为 false ,如果自定义 storage 默认为 true。
replacer: JSON_Stringify_replacer; // 仅在自定义数据存储器后生效。同 JSON.stringify 的 replacer
reviver: JSON_Parse_reviver; // 仅在自定义数据存储器后生效。同 JSON.parse 的 reviver
prefix?: string; // 缓存键前缀
};

export declare class Storage<DataType = any> {
constructor(storage?: TStorage, options?: Partial<StorageOptions>);
get(key: string): DataType;
set(key: string, data: DataType): void;
del(key: string): void;
clear(): void;
}
```
<mark>**⚠️ 注意:同一个命名空间的缓存是共享的。意味着命名空间名称相同的情况下,不同实例之间共用同一份缓存数据。建议自定义命名空间名称,避免不同实例的缓存数据冲突**</mark>
## Cache 配置项
Expand Down
8 changes: 3 additions & 5 deletions src/Cache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Emitter from 'emitter-pro';
import { getUniqueId } from './utils';
import { TStorage } from './interface';
import { Storage, StorageOptions } from './Storage';

Expand All @@ -19,7 +18,6 @@ export type CacheOptions = Omit<StorageOptions, 'memoryScope'> & {
stdTTL: number; // 数据存活时间,单位为毫秒,默认0。0表示无期限。
checkperiod: number; // 定时检查过期数据,单位毫秒。默认 0 。
storage: TStorage; // 自定义数据存储器。支持 localStorage/sessionStorage 。
prefix: string; // 缓存键前缀。
};

class Cache<ValueType = any> extends Emitter<(key: string, value: ValueType) => void> {
Expand All @@ -37,7 +35,7 @@ class Cache<ValueType = any> extends Emitter<(key: string, value: ValueType) =>
let ns = defaultNamespace,
opts: CacheOptions | undefined;
if (typeof namespace === 'string') {
ns = namespace;
ns = namespace || defaultNamespace;
} else if (typeof namespace === 'object') {
opts = namespace;
}
Expand All @@ -56,10 +54,10 @@ class Cache<ValueType = any> extends Emitter<(key: string, value: ValueType) =>
};
this.storage = new Storage(this.options.storage, {
memoryScope: ns,
...opts
...this.options
});

this.cacheKey = (this.options.prefix || '') + (ns || '') || getUniqueId();
this.cacheKey = ns;
this.startCheckperiod();
}

Expand Down
19 changes: 13 additions & 6 deletions src/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,40 @@ export type StorageOptions = {
replacer: JSON_Stringify_replacer; // 仅在自定义数据存储器后生效。同 JSON.stringify 的 replacer
reviver: JSON_Parse_reviver; // 仅在自定义数据存储器后生效。同 JSON.parse 的 reviver
memoryScope?: string; // 内存缓存域
prefix?: string; // 缓存键前缀
};

export class Storage {
export class Storage<DataType = any> {
private storage: TStorage;
private options: Partial<StorageOptions>;

constructor(storage?: TStorage, options: Partial<StorageOptions> = {}) {
const isSupported = storage ? isStorageSupported(storage) : false;
this.options = {
needParsed: isSupported,
prefix: '',
...options
};

this.storage = isSupported ? storage! : new MemoryStorage(this.options.memoryScope);
}
get(key: string) {
const data = this.storage.getItem(key);

protected getKey(key: string) {
return this.options.prefix + key;
}

get(key: string): DataType {
const data = this.storage.getItem(this.getKey(key));
return this.options.needParsed ? parse(data, this.options.reviver) : data;
}
set(key: string, data: any) {
set(key: string, data: DataType) {
this.storage.setItem(
key,
this.getKey(key),
this.options.needParsed ? stringify(data, this.options.replacer) : data
);
}
del(key: string) {
this.storage.removeItem(key);
this.storage.removeItem(this.getKey(key));
}
clear() {
if (typeof this.storage.clear === 'function') {
Expand Down
10 changes: 1 addition & 9 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ function randomString() {
return Math.random().toString(16).substring(2, 8);
}

// 内部自增id
let uid = 1;

// 返回唯一标识
export function getUniqueId() {
return `${randomString()}_${uid++}`;
}

// 是否支持 storage
export function isStorageSupported(storage: TStorage) {
try {
Expand All @@ -23,7 +15,7 @@ export function isStorageSupported(storage: TStorage) {
!!storage.getItem &&
!!storage.removeItem;
if (isSupport) {
const key = getUniqueId();
const key = randomString() + new Date().getTime();
const value = '1';
storage.setItem(key, value);
if (storage.getItem(key) !== value) {
Expand Down
4 changes: 2 additions & 2 deletions test/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe('namespace', () => {
storage: customStorage
});
// @ts-ignore
expect(myCache.cacheKey).toBe('cache2_default');
expect(myCache.cacheKey).toBe('default');

const myCache2 = new Cache({
prefix: ''
Expand All @@ -149,7 +149,7 @@ describe('namespace', () => {
prefix: ''
});
// @ts-expect-error
expect(myCache3.cacheKey.length).toBe(8);
expect(myCache3.cacheKey.length).toBe(7);
});

it('same namespace', () => {
Expand Down
13 changes: 13 additions & 0 deletions test/storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,17 @@ describe('Storage', () => {
expect(memoryStorage1.get('a')).toBe(1);
expect(memoryStorage2.get('a')).toBe(2);
});

it('prefix', () => {
const store1 = new Storage();

// @ts-ignore
expect(store1.getKey('abc')).toBe('abc');

const store2 = new Storage(undefined, {
prefix: 'cache2'
});
// @ts-ignore
expect(store2.getKey('abc')).toBe('cache2abc');
});
});

0 comments on commit 185b6a5

Please sign in to comment.