
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
perfect-cache
Advanced tools
PerfectCache 是一个前端 javascript 的缓存库,可以使用不同的存储引擎来进行数据的保存,获取,删除等操作,作为国产软件,始终追求极致与完美,所以命名PerfectCache。它拥有如下功能
整个库导出的对象包括
export {
PerfectCache, // 核心的缓存类
BaseStore, // 所有存储引擎的基类,用于自定义存储引擎
EventListener, // 事件基类,拥有$on,$off,$emit实例方法,一般不用到
systemStores, // 系统存储引擎,一般不用到
externalStores, // 注册的自定义存储引擎,一般不用到
LocalStorageStore, // 本地存储的存储引擎类,一般不用到
MemoryStore, // 内存的存储引擎类,一般不用到
SessionStorageStore, // 会话的存储引擎类,一般不用到
CookieStore, // Cookie的存储引擎类,一般不用到
IndexedDBStore, // 本地数据库的存储引擎类,一般不用到
StoreResult, // 存储结果,用于判断setItem是否成功
registerStore, // 用于注册自定义存储引擎
getSupportedDriverList, // 在使用相应的存储引擎之前可以先获取支持的情况,确保可以正常使用
connectToIndexedDB, // 封装了连接到IndexedDB函数
createDBAndObjectStores, // 封装了连接DB并创建一次性创建多个store的函数
cacheLogger, // 缓存库的日志前缀'perfect-cache:*',用于调试
indexedDBLogger, // indexedDB的日志前缀'perfect-cache:indexedDB:*',用于调试
};
整个库导出的TS类型包括
export type {
Events, // 定义了BaseStore支持的事件类型的格式
SupportedDriver, // 定义了默认支持的存储引擎类型
BaseStoreOptions, // 定义了BaseStore的构造函数的选项
CacheOptions, // 定义了PerfectCache的构造函数的选项
IndexedDBStoreOptions, // 定义了IndexedDBStore的构造函数的选项
IndexedDBStoreObject, // 定义了IndexedDBStore的内部存储结构
StoreObject, // 定义了存储引擎的内部存储结构
SetItemOptions, // 定义了setItem函数的选项
GetItemOptions, // 定义了getItem函数的选项
KeyFallbackConfig, // 定义了key字符串的退路函数配置
KeyRegexFallbackConfig, // 定义了key正则表达式的退路函数配置
};
// 方式一,传driver和选项,推荐
const perfectCacheInstance = new PerfectCache(driver, opts);
// 方式二,只传选项
const perfectCacheInstance = new PerfectCache(opts);
// 方式三,只适合使用默认使用内存driver
const perfectCacheInstance = new PerfectCache();
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| driver | 使用的存储引擎 | string | memory | 非必填 |
| opts | 缓存配置 | object | - | 非必填 |
| opts.driver | 缓存使用的存储引擎 | string | memory | 非必填 |
| opts.prefix | 缓存 key 使用的前缀 | string | cache: | 非必填 |
| opts.dbName | indexdb 名称 | string | 'perfect-cache' | 非必填 |
| opts.objectStoreName | indexdb 的 store 名称 | string | 'perfect-cache' | 非必填 |
| opts.dbVersion | indexdb 连接版本,默认最新 | number | - | 非必填 |
| opts.dbConnection | indexdb 连接,用于公用现有连接 | IDBDatabase | - | 非必填 |
| opts.initStoreImmediately | 是否立即初始化存储引擎 | boolean | true | 非必填 |
实例在构造函数时默认立即调用init并马上ready,但是也可以设置手动调用,把initStoreImmediately设置为false,并在你认为合适的时机才调用init方法进行存储引擎的初始化,此时如果不调用init函数进行初始化,存储引擎会一直处于非ready的状态,所有的操作都会被阻塞。
perfectCacheInstance.init();
const isReady = await perfectCacheInstance.isReady();
// 方式一,回调Callback方式
perfectCacheInstance.ready((self) => {
window.console.log(self.driver + ' cache ready.');
});
// 方式二,promise模式
perfectCacheInstance.ready().then(() => {
window.console.log('cache ready.');
});
const value = await perfectCacheInstance.getItem(key, opts);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| key | 缓存的 key | string | - | 必填 |
| opts | 获取选项 | object | - | 非必填 |
| opts.defaultVal | 获取不到时返回的默认值 | any | - | 非必填 |
| opts.withFallback | 获取不到时是否使用退路 | boolean | true | 非必填 |
| opts.refreshCache | 使用退路获取到值时是否更新缓存 | boolean | true | 非必填 |
/**
* itemListMap = {
* 'key_a': 'valueA',
* 'key_b': undefined,
* 'otherKey_c': 'valueC'
* }
*/
const itemListMap = await perfectCacheInstance.getItemList(['key_a', 'key_b', 'otherKey_c'], opts);
/**
* itemListMap = {
* 'key_a': 'valueA',
* 'key_b': undefined,
* }
*/
const itemListMap = await perfectCacheInstance.getItemList(/^key_.*$/, opts);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| keys | 缓存的 keys 数组或者 key 正则匹配表达式 | Array<string>|RegExp | - | 必填 |
| opts | 获取选项 | object | - | 非必填 |
| opts.defaultVal | 获取不到时返回的默认值 | any | - | 非必填 |
| opts.withFallback | 获取不到时是否使用退路 | boolean | true | 非必填 |
| opts.refreshCache | 使用退路获取到值时是否更新缓存 | boolean | true | 非必填 |
/**
* itemListMap = {
* 'key_a': 'valueA',
* 'key_b': undefined,
* 'otherKey_c': 'valueC'
* }
*/
const itemListMap = await perfectCacheInstance.getAllItem();
const result = await perfectCacheInstance.setItem(key, value, options);
参数说明,说明一下,在没有设置 expiredTime 和 expiredTimeAt 参数的情况下,maxAge 参数可以每次 getItem 获取完 key 的值,都会延迟过期时间自动续期,而设置 expiredTime 则不会自动续期
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| key | 设置的 key | string | - | 必填 |
| value | 设置的缓存值 | any | - | 必填 |
| options | 设置选项 | object | - | 非必填 |
| options.expiredTime | 过期时间,毫秒数 | number | - | 非必填 |
| options.expiredTimeAt | 过期时间戳 | number | - | 非必填 |
| options.maxAge | 缓存最大生命时间戳 | number | - | 非必填 |
| options.setOnlyNotExist | 只有不存在 key 才设置 | boolean | false | 非必填 |
| options.setOnlyExist | 只有存在 key 才设置 | boolean | false | 非必填 |
设置的返回结果枚举值
import { StoreResult } from 'perfect-cache';
switch (result) {
// 成功设置
case StoreResult.OK:
break;
// 设置缓存值时,设置了不存在才设置,但是由于 key 存在导致没有设置
case StoreResult.NX_SET_NOT_PERFORMED:
break;
// 置缓存值时,设置了存在才设置,但是由于 key 不存在导致没有设置
case StoreResult.XX_SET_NOT_PERFORMED:
break;
}
| 名称 | 意义 |
|---|---|
| OK | 成功设置 |
| NX_SET_NOT_PERFORMED | 设置缓存值时,设置了不存在才设置,但是由于 key 存在导致没有设置 |
| XX_SET_NOT_PERFORMED | 设置缓存值时,设置了存在才设置,但是由于 key 不存在导致没有设置 |
itemListMap = {
key_a: 'valueA',
key_b: undefined,
otherKey_c: 'valueC',
};
await perfectCacheInstance.setItemList(itemListMap, { maxAge: 5000 });
await perfectCacheInstance.removeItem(key);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| key | 缓存的 key | string | - | 必填 |
await perfectCacheInstance.removeItemList(['key_a', 'key_b', 'otherKey_c']);
await perfectCacheInstance.removeItemList(/^key_.*$/);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| keys | 缓存的 keys 数组或者 key 正则匹配表达式 | Array<string>|RegExp | - | 必填 |
await perfectCacheInstance.clear();
const isKeyExists = await perfectCacheInstance.existsKey(key);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| key | 缓存的 key | string | - | 必填 |
const keys = await perfectCacheInstance.keys();
const keysCount = await perfectCacheInstance.length();
perfectCacheInstance.fallbackKey(key, fallback, options);
// 当获取key=userInfo找不到时,则调用fallback退路函数来获取userInfo,通过Promise返回新的缓存值,并设置有效期2小时
perfectCacheInstance.fallbackKey(
'userInfo',
() => {
return new Promise((resolve) => {
window.setTimeout(() => {
resolve({
userName: '张三',
userCode: '123',
});
}, 3000);
});
},
{ expiredTime: 7200 * 1000 }
);
// 当获取key匹配page_找不到时,则调用fallback退路函数来获取pageInfo,通过Promise返回新的缓存值,并设置生命存活期2小时
perfectCacheInstance.fallbackKey(
/^page_(\w+)$/i,
(key) => {
return new Promise((resolve) => {
const pageCode = key.match(/^page_(\w+)$/i)[1];
api.getPageInfo(pageCode).then((pageInfo) => {
resolve(pageInfo);
});
});
},
{ maxAge: 7200 * 1000 }
);
参数说明
| 名称 | 意义 | 类型 | 默认值 | 是否必填 |
|---|---|---|---|---|
| key | 缓存的 key 或者缓存的 key 匹配正则表达式 | string/Regex | - | 必填 |
| fallback | 退路函数 | (key:string)=>Promise<any> | - | 必填 |
| options | 通过退路函数获取到的缓存值更新缓存的参数 | object | - | 非必填 |
| options.expiredTime | 过期时间,毫秒数 | number | - | 非必填 |
| options.maxAge | 缓存最大生命时间戳 | number | - | 非必填 |
const cacheExpiredHandler = (key) => {
console.log('key expired', key);
};
perfectCacheInstance.$on('ready', () => {});
perfectCacheInstance.$on('cacheExpired', cacheExpiredHandler);
perfectCacheInstance.$off('cacheExpired', cacheExpiredHandler);
perfectCacheInstance.$on('myEvent', (e) => {
console.log(e); // e={ a: 1, b: 2 }
});
perfectCacheInstance.$emit('myEvent', { a: 1, b: 2 });
| 名称 | 触发时机 | 返回数据 |
|---|---|---|
| ready | 当存储引擎准备好了可以存取数据的时候触发 | - |
| cacheExpired | 当获取的 key 过期时触发 | key |
目前浏览器端支持内存(memory),本地存储(localStorage),会话(sessionStorage),cookie(cookie),数据库(indexedDB) 这些存储引擎,同时还支持自定义存储引擎。可以通过
import { getSupportedDriverList } from 'perfect-cache';
const supportedDriverList = getSupportedDriverList();
获取到支持的存储引擎,包括了默认的系统引擎和自定义引擎
import { registerStore, BaseStore } from 'perfect-cache';
class MyStore extends BaseStore {
// 引擎名称
static driver = 'myStore';
data = {};
constructor(opts) {
super(opts);
}
// 可选覆盖初始化函数
init() {
// 告诉缓存系统已经准备好了,如果是异步的就在准备好的时候调用ready函数告知系统
this.getReady();
return this;
}
// 必须重载keyValueGet方法,告知引擎底层怎样获取一个key对应的缓存值
// 必须返回Promise对象,同时resolve的对象结构为
// { value:'xxx', expiredTimeAt: 12345678910, maxAge: 3600000}
keyValueGet(key) {
const valueStr = this.data[key];
return new Promise((resolve) => {
if (valueStr) {
try {
const valueObj = JSON.parse(valueStr);
resolve(valueObj);
} catch (error) {
window.console.debug(`get key ${key} json parse error`, valueStr);
resolve(undefined);
}
} else {
resolve(undefined);
}
});
}
// 必须重载keyValueSet方法,告知引擎底层怎样设置一个key对应的缓存值
// 必须返回Promise对象
// value的数据结构为{ value:'xxx', expiredTimeAt: 12345678910, maxAge: 3600000}
keyValueSet(key, value) {
this.data[key] = JSON.stringify(value);
return Promise.resolve();
}
// 可选覆盖实现getAllItem方法
getAllItem() {
return { ...this.data };
}
// 可选覆盖实现getItemList方法
async getItemList(keys, opts) {
let storeKeys = [];
const itemListMap = {};
if (Array.isArray(keys)) {
storeKeys = keys;
} else {
if (keys instanceof RegExp) {
storeKeys = (await this.keys()).filter((key) => {
return keys.test(key);
});
}
}
for (const key of storeKeys) {
itemListMap[key] = this.data[key];
}
return itemListMap;
}
// 可选覆盖实现setItemList方法
async setItemList(itemList, options) {
const keys = Object.keys(itemList);
for (const key of keys) {
const value = itemList[key];
await this.setItem(key, value, options);
}
}
// 必须重载removeItem方法,删除key对应的缓存值
removeItem(key) {
return new Promise((resolve, reject) => {
try {
delete this.data[key];
resolve();
} catch (error) {
reject(error);
}
});
}
// 可选覆盖实现removeItemList方法
async removeItemList(keys) {
let storeKeys = [];
if (Array.isArray(keys)) {
storeKeys = keys;
} else {
if (keys instanceof RegExp) {
storeKeys = (await this.keys()).filter((key) => {
return keys.test(key);
});
}
}
for (const key of storeKeys) {
delete this.data[key];
}
}
// 可选覆盖实现clear方法,清空缓存值
clear() {
this.data = {};
return Promise.resolve();
}
// 必须重载existsKey方法,告知引擎底层一个key是否存在
// 必须返回Promise对象
existsKey(key) {
if (key in this.data) {
return Promise.resolve(true);
} else {
return Promise.resolve(false);
}
}
// 必须重载keys方法,获取缓存所有key
keys() {
const keys = Object.keys(this.data);
return Promise.resolve(keys);
}
// 可选覆盖实现length方法,获取keys的长度
length() {
return Promise.resolve(Object.keys(this.data).length);
}
}
registerStore(MyStore);
const perfectCacheInstance = new PerfectCache('myStore');
FAQs
browser cache lib
We found that perfect-cache demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.