我们知道 localStorage 中的数据是长期保存的,除非手动删除,否则他会一直存在。如果我们想实现一种数据有过期时间的存储方式,该怎么实现呢? 首先应该想到的是 cookie,cookie 本身就有有效期的配置,当某个 cookie 时,浏览器自动清理该 cookie。可是使用 cookie 存储数据,有个不好的地方,很多我们存储的数据,本就是我们前端自己用到的,后端根本用不到。可是存储到 cookie 中后,页面中所有的 cookie 都会随着请求发送给后端,造成传输的 cookie 比较长,而且没有必要。 因此,我们可以基于 localStorage 来实现一套这样的有过期时间的存储方式。我们在之前的文章 如何重写 localStorage 中的方法 中,也了解了一些重写 localStorage 的方法。这里我们是自己在外层封装一层的方式,来调用 localStorage。 我这里封装的类名叫: 我们为了实现跟 localStorage 使用上的一致性体验,这里我们自己的 api 名称和实现方式跟 localStorage 基本一致。 可以看到我们实现的类里,有三个变化: 上面是我们的大致框架,接下来我们来具体实现下这些方法。 接下来我们来一一实现这些方法。 这里我们新增了一个 options 参数,用来配置过期时间: 假如两个属性都设置了,我这里约定 expired 属性的优先级更高一些。 我们在过期时间的实现过程中,目前只支持了 number 类型,即需要传入一个时间戳,参与运算。您也可以扩展到 string 类型(比如'2024/11/23 14:45:34')或者其他格式 设置好过期时间后,我们将 value,存储的起始时间和过期时间,转义成 json string 存储起来。我们这里用了 dayjs 对时间戳进行格式化,方便开发者可以快速地识别。若没有这个需要,也可以直接存储时间戳,减少第三方类库的依赖。 该方法并没有支持永久存储的设定,若您需要永久存储,可以直接使用 localStorage 来存储。 获取某 key 存储的值,主要是对过期时间的判断。 在获取 key 时,主要经过 3 个过程: 这里我们在删除数据时,使用了 localStorage 中的数据并不会自动清理,我们需要一个方法用来手动批量清理已过期的数据。 在项目的入口处添加上该方法,用户每次进入项目时,都会自动清理一次已过期的 key。 上面我们是分步讲解的,这里我们放下完整的代码。同时,我也在 GitHub 上放了一份:wenzi0github/local-expired-storage。 使用: 这个功能本身不难,也有很多开发者自己实现过。这里我也是总结下之前实现的过程。LocalExpiredStorage
,即有过期时间的 localStorage。1. 实现与 localStorage 基本一致的 api #
interface SetItemOptions {
maxAge?: number; // 从当前时间往后多长时间过期
expired?: number; // 过期的准确时间点,优先级比maxAge高
}
class LocalExpiredStorage {
private prefix = "local-expired-"; // 用于跟没有过期时间的key进行区分
constructor(prefix?: string) {
if (prefix) {
this.prefix = prefix;
}
}
setItem(key: string, value: any, options?: SetItemOptions) {}
getItem(key: string): any {}
removeItem(key: string) {}
clearAllExpired() {}
}
const localExpiredStorage = new LocalExpiredStorage();
export default localExpiredStorage;
2. 具体实现 #
2.1 setItem #
class LocalExpiredStorage {
private prefix = "local-expired-"; // 用于跟没有过期时间的key进行区分
constructor(prefix?: string) {
if (prefix) {
this.prefix = prefix;
}
}
setItem(key: string, value: any, options?: SetItemOptions) {
const now = Date.now();
let expired = now + 1000 * 60 * 60 * 3; // 默认过期时间为3个小时
// 这里我们限定了 expired 和 maxAge 都是 number 类型,
// 您也可以扩展支持到 string 类型或者如 { d:2, h:3 } 这种格式
if (options?.expired) {
expired = options?.expired;
} else if (options?.maxAge) {
expired = now + options.maxAge;
}
// 我们这里用了 dayjs 对时间戳进行格式化,方便快速识别
// 若没这个需要,也可以直接存储时间戳,减少第三方类库的依赖
localStorage.setItem(
`${this.prefix}${key}`,
JSON.stringify({
value,
start: dayjs().format("YYYY/MM/DD hh:mm:ss"), // 存储的起始时间
expired: dayjs(expired).format("YYYY/MM/DD hh:mm:ss"), // 存储的过期时间
})
);
}
}
{ d:2, h:3 }
这种格式。2.2 getItem #
class LocalExpiredStorage {
private prefix = "local-expired-"; // 用于跟没有过期时间的key进行区分
constructor(prefix?: string) {
if (prefix) {
this.prefix = prefix;
}
}
getItem(key: string): any {
const result = localStorage.getItem(`${this.prefix}${key}`);
if (!result) {
// 若key本就不存在,直接返回null
return result;
}
const { value, expired } = JSON.parse(result);
if (Date.now() <= dayjs(expired).valueOf()) {
// 还没过期,返回存储的值
return value;
}
// 已过期,删除该key,然后返回null
this.removeItem(key);
return null;
}
removeItem(key: string) {
localStorage.removeItem(`${this.prefix}${key}`);
}
}
this.removeItem()
,即自己实现的删除方法。本来我们也是要实现这个方法的,那就直接使用了吧。2.3 clearAllExpired #
class LocalExpiredStorage {
private prefix = "local-expired-"; // 用于跟没有过期时间的key进行区分
clearAllExpired() {
let num = 0;
// 判断 key 是否过期,然后删除
const delExpiredKey = (key: string, value: string | null) => {
if (value) {
// 若value有值,则判断是否过期
const { expired } = JSON.parse(value);
if (Date.now() > dayjs(expired).valueOf()) {
// 已过期
localStorage.removeItem(key);
return 1;
}
} else {
// 若 value 无值,则直接删除
localStorage.removeItem(key);
return 1;
}
return 0;
};
const { length } = window.localStorage;
const now = Date.now();
for (let i = 0; i < length; i++) {
const key = window.localStorage.key(i);
if (key?.startsWith(this.prefix)) {
// 只处理我们自己的类创建的key
const value = window.localStorage.getItem(key);
num += delExpiredKey(key, value);
}
}
return num;
}
}
3. 完整的代码 #
interface SetItemOptions {
maxAge?: number; // 从当前时间往后多长时间过期
expired?: number; // 过期的准确时间点,优先级比maxAge高
}
class LocalExpiredStorage {
private prefix = "local-expired-"; // 用于跟没有过期时间的key进行区分
constructor(prefix?: string) {
if (prefix) {
this.prefix = prefix;
}
}
// 设置数据
setItem(key: string, value: any, options?: SetItemOptions) {
const now = Date.now();
let expired = now + 1000 * 60 * 60 * 3; // 默认过期时间为3个小时
// 这里我们限定了 expired 和 maxAge 都是 number 类型,
// 您也可以扩展支持到 string 类型或者如 { d:2, h:3 } 这种格式
if (options?.expired) {
expired = options?.expired;
} else if (options?.maxAge) {
expired = now + options.maxAge;
}
// 我们这里用了 dayjs 对时间戳进行格式化,方便快速识别
// 若没这个需要,也可以直接存储时间戳,减少第三方类库的依赖
localStorage.setItem(
`${this.prefix}${key}`,
JSON.stringify({
value,
start: dayjs().format("YYYY/MM/DD hh:mm:ss"), // 存储的起始时间
expired: dayjs(expired).format("YYYY/MM/DD hh:mm:ss"), // 存储的过期时间
})
);
}
getItem(key: string): any {
const result = localStorage.getItem(`${this.prefix}${key}`);
if (!result) {
// 若key本就不存在,直接返回null
return result;
}
const { value, expired } = JSON.parse(result);
if (Date.now() <= dayjs(expired).valueOf()) {
// 还没过期,返回存储的值
return value;
}
// 已过期,删除该key,然后返回null
this.removeItem(key);
return null;
}
// 删除key
removeItem(key: string) {
localStorage.removeItem(`${this.prefix}${key}`);
}
// 清除所有过期的key
clearAllExpired() {
let num = 0;
// 判断 key 是否过期,然后删除
const delExpiredKey = (key: string, value: string | null) => {
if (value) {
// 若value有值,则判断是否过期
const { expired } = JSON.parse(value);
if (Date.now() > dayjs(expired).valueOf()) {
// 已过期
localStorage.removeItem(key);
return 1;
}
} else {
// 若 value 无值,则直接删除
localStorage.removeItem(key);
return 1;
}
return 0;
};
const { length } = window.localStorage;
const now = Date.now();
for (let i = 0; i < length; i++) {
const key = window.localStorage.key(i);
if (key?.startsWith(this.prefix)) {
// 只处理我们自己的类创建的key
const value = window.localStorage.getItem(key);
num += delExpiredKey(key, value);
}
}
return num;
}
}
const localExpiredStorage = new LocalExpiredStorage();
export default localExpiredStorage;
localExpiredStorage.setItem("key", "value", { maxAge: 5000 }); // 有效期为5000毫秒
localExpiredStorage.setItem("key", "value", {
expired: Date.now() + 1000 * 60 * 60 * 12,
}); // 有效期为 12 个小时,自己计算到期的时间戳
// 获取数据
localExpiredStorage.getItem("key");
// 删除数据
localExpiredStorage.removeItem("key");
// 清理所有过期的key
localExpiredStorage.clearAllExpired();
4. 总结 #
版权属于:
加速器之家
作品采用:
《
署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
》许可协议授权
评论