Skip to content

Commit

Permalink
feat: 二次封装localforage支持设置过期时间,提供完整的类型提示
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoxian521 committed Sep 27, 2023
1 parent 320ee2b commit e10c916
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"intro.js": "^7.2.0",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.5",
"localforage": "^1.10.0",
"md-editor-v3": "2.7.2",
"mint-filter": "^4.0.3",
"mitt": "^3.0.1",
Expand Down
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions src/utils/localforage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import forage from "localforage";
import { LocalForage, ProxyStorage, ExpiresData } from "./types.d";

class StorageProxy implements ProxyStorage {
protected storage: LocalForage;
constructor(storageModel) {
this.storage = storageModel;
this.storage.config({
// driver: [forage.LOCALSTORAGE],
name: "pure-admin"
});
}

/**
* @description 将对应键名的数据保存到离线仓库
* @param k 键名
* @param v 键值
* @param m 缓存时间(单位`分`,默认`0`分钟,永久缓存)
*/
public async setItem<T>(k: string, v: T, m = 0): Promise<T> {
return new Promise((resolve, reject) => {
this.storage
.setItem(k, {
data: v,
expires: new Date().getTime() + m * 60 * 1000
})
.then(value => {
resolve(value.data);
})
.catch(err => {
reject(err);
});
});
}

/**
* @description 从离线仓库中获取对应键名的值
* @param k 键名
*/
public async getItem<T>(k: string): Promise<T> {
return new Promise((resolve, reject) => {
this.storage
.getItem(k)
.then((value: ExpiresData<T>) => {
const data =
value.expires > new Date().getTime() || value.expires === 0
? value.data
: null;
resolve(data);
})
.catch(err => {
reject(err);
});
});
}

/**
* @description 从离线仓库中删除对应键名的值
* @param k 键名
*/
public async removeItem(k: string) {
return new Promise<void>((resolve, reject) => {
this.storage
.removeItem(k)
.then(() => {
resolve();
})
.catch(err => {
reject(err);
});
});
}

/**
* @description 从离线仓库中删除所有的键名,重置数据库
*/
public async clear() {
return new Promise<void>((resolve, reject) => {
this.storage
.clear()
.then(() => {
resolve();
})
.catch(err => {
reject(err);
});
});
}
}

/**
* 二次封装 [localforage](https://localforage.docschina.org/) 支持设置过期时间,提供完整的类型提示
*/
export const localForage = () => new StorageProxy(forage);
166 changes: 166 additions & 0 deletions src/utils/localforage/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// https://github.com/localForage/localForage/blob/master/typings/localforage.d.ts

interface LocalForageDbInstanceOptions {
name?: string;

storeName?: string;
}

interface LocalForageOptions extends LocalForageDbInstanceOptions {
driver?: string | string[];

size?: number;

version?: number;

description?: string;
}

interface LocalForageDbMethodsCore {
getItem<T>(
key: string,
callback?: (err: any, value: T | null) => void
): Promise<T | null>;

setItem<T>(
key: string,
value: T,
callback?: (err: any, value: T) => void
): Promise<T>;

removeItem(key: string, callback?: (err: any) => void): Promise<void>;

clear(callback?: (err: any) => void): Promise<void>;

length(callback?: (err: any, numberOfKeys: number) => void): Promise<number>;

key(
keyIndex: number,
callback?: (err: any, key: string) => void
): Promise<string>;

keys(callback?: (err: any, keys: string[]) => void): Promise<string[]>;

iterate<T, U>(
iteratee: (value: T, key: string, iterationNumber: number) => U,
callback?: (err: any, result: U) => void
): Promise<U>;
}

interface LocalForageDropInstanceFn {
(
dbInstanceOptions?: LocalForageDbInstanceOptions,
callback?: (err: any) => void
): Promise<void>;
}

interface LocalForageDriverMethodsOptional {
dropInstance?: LocalForageDropInstanceFn;
}

// duplicating LocalForageDriverMethodsOptional to preserve TS v2.0 support,
// since Partial<> isn't supported there
interface LocalForageDbMethodsOptional {
dropInstance: LocalForageDropInstanceFn;
}

interface LocalForageDriverDbMethods
extends LocalForageDbMethodsCore,
LocalForageDriverMethodsOptional {}

interface LocalForageDriverSupportFunc {
(): Promise<boolean>;
}

interface LocalForageDriver extends LocalForageDriverDbMethods {
_driver: string;

_initStorage(options: LocalForageOptions): void;

_support?: boolean | LocalForageDriverSupportFunc;
}

interface LocalForageSerializer {
serialize<T>(
value: T | ArrayBuffer | Blob,
callback: (value: string, error: any) => void
): void;

deserialize<T>(value: string): T | ArrayBuffer | Blob;

stringToBuffer(serializedString: string): ArrayBuffer;

bufferToString(buffer: ArrayBuffer): string;
}

interface LocalForageDbMethods
extends LocalForageDbMethodsCore,
LocalForageDbMethodsOptional {}

export interface LocalForage extends LocalForageDbMethods {
LOCALSTORAGE: string;
WEBSQL: string;
INDEXEDDB: string;

/**
* Set and persist localForage options. This must be called before any other calls to localForage are made, but can be called after localForage is loaded.
* If you set any config values with this method they will persist after driver changes, so you can call config() then setDriver()
* @param {LocalForageOptions} options?
*/
config(options: LocalForageOptions): boolean;
config(options: string): any;
config(): LocalForageOptions;

/**
* Create a new instance of localForage to point to a different store.
* All the configuration options used by config are supported.
* @param {LocalForageOptions} options
*/
createInstance(options: LocalForageOptions): LocalForage;

driver(): string;

/**
* Force usage of a particular driver or drivers, if available.
* @param {string} driver
*/
setDriver(
driver: string | string[],
callback?: () => void,
errorCallback?: (error: any) => void
): Promise<void>;

defineDriver(
driver: LocalForageDriver,
callback?: () => void,
errorCallback?: (error: any) => void
): Promise<void>;

/**
* Return a particular driver
* @param {string} driver
*/
getDriver(driver: string): Promise<LocalForageDriver>;

getSerializer(
callback?: (serializer: LocalForageSerializer) => void
): Promise<LocalForageSerializer>;

supports(driverName: string): boolean;

ready(callback?: (error: any) => void): Promise<void>;
}

// Customize

export interface ProxyStorage {
setItem<T>(k: string, v: T, m: number): Promise<T>;
getItem<T>(k: string): Promise<T>;
removeItem(k: string): Promise<void>;
clear(): Promise<void>;
}

export interface ExpiresData<T> {
data: T;
expires: number;
}

1 comment on commit e10c916

@xiaoxian521
Copy link
Member Author

@xiaoxian521 xiaoxian521 commented on e10c916 Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果你有大量数据存储到浏览器本地的需求,localforage 很合适,需要注意它的所有操作都是异步的

可以存储的数据类型如下
Array
ArrayBuffer
Blob
Float32Array
Float64Array
Int8Array
Int16Array
Int32Array
Number
Object
Uint8Array
Uint8ClampedArray
Uint16Array
Uint32Array
String

Please sign in to comment.