如何让localStorage支持过期时间设置
前言
最近在项目开发中,遇见了大家都常会遇见的问题,让本地存储支持过期时间的设置。那相信学习前端的童鞋们都知道,我们经常是会用的 cookie 、localStorage 、sessionStorage ,它们三个都是可以用来存放数据的。这里我们就不细讲它们之间的区别,主要讲当我们设置localStorage 的时候,怎么来给它设置一个过期时间了?下来我们就开始今天的主题。封装
根据一般的业务需求,我们需要支持`新增,修改,删除`,按照初步设想,开始编写我们代码。可跳过中间部分,直接查看最后的完整版代码示例。(TS目前还有不太熟练的地方,有什么地方有问题欢迎指出)// 定义类 class NStorage { storage: Storage; constructor(name?: string) { this.storage = window[name as keyof typeof window] || window.localStorage } /** * @description 存储方法 * @param {String} key 键名 * @param {any} value 值 * @param {number} [expires] 可选,默认0,永久 */ set(key: string, value: any, expires: number = 0) { const storeValue = { value, startTime: new Date().getTime(), __expires__: expires } this.storage.setItem(key, JSON.stringify(storeValue)) } /** * @description 获取方法 * @param {String} key 键名 * @returns {any} 值 */ get(key: string) { try { const storeItem = this.storage.getItem(key) || "{}" const storeValue = JSON.parse(storeItem) const time = new Date().getTime() // 如果是永久,则直接返回 if (storeValue?.__expires__ === 0) return storeValue // 判断当前时间是否过期 if (storeValue?.startTime && (time - storeValue?.startTime >= storeValue?.__expires__)) { this.remove(key); return null; } return storeValue.value } catch (error) { return null } } /** * @description 删除方法 * @param {String} key 删除键名 */ remove(key: string) { key && this.storage.removeItem(key); } /** * @description 清除所有本地存储 */ clear() { this.storage.clear(); } }
下面是本地测试:const store = new NStorage() store.set("name", "小红", { expires: 10 }) let count = 0 const time = setInterval(() => { const name = store.get("name") console.log(name) count += 1 if (count > 10) { clearInterval(time) } }, 1000)
在控制台可以看见打印的 小红 ,3秒后localStorage 里面的值就会被删除。功能我们是实现啦,是不是就可以皆大欢喜了?有没有bug?有没有可以优化的问题了?那当然是有的了:
1. 设置的过期时间,表示的是秒?分?小时?
2. 如果多次设置同一个值,那么依据现在逻辑,每次都会重新计算时间,但是我可能只是更新值,时间不需要重新计算了?优化
定义出选项类型// 定义支持时间类型,依次:年、月、日、时、分、秒 type TUnitType = "Y" | "M" | "D" | "h" | "m" | "s" // 定义可选参类型 interface IExtraOptions { expires?: number; unit?: TUnitType, reset?: boolean } // 设置默认初始值 const EXTRA_OPTIONS: IExtraOptions = { expires: 0, // 默认永久 unit: "s", // 默认秒 reset: false // 是否重置时间 } // 根据单位和传入时间,计算出毫秒级时间 function calcTime(time: number, unit: TUnitType = "s") { let newTime = 0 switch (unit) { case "Y": newTime = time * 365 * 24 * 60 * 60 * 1000 break; case "M": // 注意⚠️:月份这里偷懒,直接使用每月30天计算 newTime = time * 30 * 24 * 60 * 60 * 1000 break; case "D": newTime = time * 24 * 60 * 60 * 1000 break; case "h": newTime = time * 60 * 60 * 1000 break; case "m": newTime = time * 60 * 1000 break; case "s": newTime = time * 1000 break; default: newTime = time break; } return newTime } **修改 set、get 方法** /** * @description 存储方法 * @param {String} key 键名 * @param {any} value 值 * @param {number} [options] 可选 * @param {Number} [options.expires] 默认0,永久 * @param {String} [options.unit] 默认"s",秒 * @param {Boolean} [options.reset] 默认false,不重置 */ set(key: string, value: any, options?: IExtraOptions) { const isExist = this.hasKey(key) const extra = Object.assign(EXTRA_OPTIONS, options) if (isExist) { // 如果存在,判断是否是需要重新设置值 const sValue = this.get(key, true) sValue.value = value sValue.__expires__ = calcTime(extra.expires || sValue.__expires__, extra.unit || sValue.__unit__) if (extra.reset) { // 存在且重新设置时间 sValue.startTime = new Date().getTime() this._set(key, sValue) } else { // 如果已经存在,且不重新计算时间,则直接修改值后存储 this._set(key, sValue) } } else { const storeValue = { value, startTime: new Date().getTime(), __expires__: calcTime(extra.expires as number, extra.unit), __unit__: extra.unit }; this._set(key, storeValue) } } /** * @description set内部方法 * @param {String} key 键名 * @param {any} value 值 */ private _set(key: string, value: any) { this.storage.setItem(key, JSON.stringify(value)); } /** * @description 获取方法 * @param {String} key 键名 * @param {Boolean} [isMerge] 可选,是否获取处理后的对象 * @returns {any} 值 */ get(key: string, isMerge: boolean = false) { try { // 不存在直接返回 null if (!key) return null const storeItem = this.storage.getItem(key) || "{}" const storeValue = JSON.parse(storeItem) const time = new Date().getTime() // 如果是永久,则直接返回 if (storeValue?.__expires__ === 0) return isMerge ? storeValue : storeValue.value; // 判断当前时间是否过期,过期则删除当前存储值 if (storeValue?.startTime && (time - storeValue?.startTime >= storeValue?.__expires__)) { this.remove(key); return null; } return isMerge ? storeValue : storeValue.value; } catch (error) { return null } } /** * @description 是否存在 * @param {String} key 键 * @returns {Boolean} true|false */ hasKey(key: string) { if (!key) return false const value = this.get(key) return value ? true : false }优化后使用介绍const store = new NStorage() // 普通设置,不过期 store.set("name", "小红") // 设置过期时间5秒 store.set("name", "小红", { expires: 5 }) // 设置过期时间5分钟,其他更换单位即可 store.set("name", "小红", { expires: 5, unit: "m" }) // 重新计算有效时长(就相当于在设置的时刻起,再往后延长之前的设置时间) store.set("name", "小红", { reset: true }) // 获取值 store.get("name") // 删除值 store.remove("name") // 删除所有值 store.clear() // 当前存储值中是否存在 store.hasKey("name")完整版代码// 定义支持时间类型,依次:年、月、日、时、分、秒 type TUnitType = "Y" | "M" | "D" | "h" | "m" | "s" // 定义可选参类型 interface IExtraOptions { expires?: number; unit?: TUnitType, reset?: boolean } // 设置默认初始值 const EXTRA_OPTIONS: IExtraOptions = { expires: 0, // 默认永久 unit: "s", // 默认秒 reset: false // 是否重置时间 } // 根据单位和传入时间,计算出毫秒级时间 function calcTime(time: number, unit: TUnitType = "s") { let newTime = 0 switch (unit) { case "Y": newTime = time * 365 * 24 * 60 * 60 * 1000 break; case "M": // 月份这里偷懒,直接使用每月30天计算 newTime = time * 30 * 24 * 60 * 60 * 1000 break; case "D": newTime = time * 24 * 60 * 60 * 1000 break; case "h": newTime = time * 60 * 60 * 1000 break; case "m": newTime = time * 60 * 1000 break; case "s": newTime = time * 1000 break; default: newTime = time break; } return newTime } class NStorage { storage: Storage; constructor(name?: string) { this.storage = window[name as keyof typeof window] || window.localStorage } /** * @description 存储方法 * @param {String} key 键名 * @param {any} value 值 * @param {number} [options] 可选 * @param {Number} [options.expires] 默认0,永久 * @param {String} [options.unit] 默认"s",秒 * @param {Boolean} [options.reset] 默认false,不重置 */ set(key: string, value: any, options?: IExtraOptions) { const isExist = this.hasKey(key) const extra = Object.assign(EXTRA_OPTIONS, options) if (isExist) { // 如果存在,判断是否是需要重新设置值 const sValue = this.get(key, true) sValue.value = value sValue.__expires__ = calcTime(extra.expires || sValue.__expires__, extra.unit || sValue.__unit__) if (extra.reset) { // 存在且重新设置时间 sValue.startTime = new Date().getTime() this._set(key, sValue) } else { // 如果已经存在,且不重新计算时间,则直接修改值后存储 this._set(key, sValue) } } else { const storeValue = { value, startTime: new Date().getTime(), __expires__: calcTime(extra.expires as number, extra.unit), __unit__: extra.unit }; this._set(key, storeValue) } } /** * @description set内部方法 * @param {String} key 键名 * @param {any} value 值 */ private _set(key: string, value: any) { this.storage.setItem(key, JSON.stringify(value)); } /** * @description 获取方法 * @param {String} key 键名 * @param {Boolean} [isMerge] 可选,是否获取处理后的对象 * @returns {any} 值 */ get(key: string, isMerge: boolean = false) { try { // 不存在直接返回 null if (!key) return null const storeItem = this.storage.getItem(key) || "{}" const storeValue = JSON.parse(storeItem) const time = new Date().getTime() // 如果是永久,则直接返回 if (storeValue?.__expires__ === 0) return isMerge ? storeValue : storeValue.value; // 判断当前时间是否过期,过期则删除当前存储值 if (storeValue?.startTime && (time - storeValue?.startTime >= storeValue?.__expires__)) { this.remove(key); return null; } return isMerge ? storeValue : storeValue.value; } catch (error) { return null } } /** * @description 删除方法 * @param {String} key 删除键名 */ remove(key: string) { key && this.storage.removeItem(key); } /** * @description 清除所有本地存储 */ clear() { this.storage.clear(); } /** * @description 是否存在 * @param {String} key 键 * @returns {Boolean} true|false */ hasKey(key: string) { if (!key) return false const value = this.get(key) return value ? true : false } }最后
里面的逻辑也可根据需求自己添加修改,代码测试我也可能存在未测到的地方,如果有问题欢迎各位大大指出!
多事之秋,2022保险大事盘点,还有更大的大事么?2022年还剩最后10天这是多事之秋的一年系列大事深刻地影响着保险业01大批保险公司评级不达标据银保监会公布2022年第三季度181家纳入评审的公司15家得C,9家得DCD合计共有
日本央行货币政策突变图为行人走在日本东京站丸之内出口附近的马路上。孙佳林摄(新华社发)12月20日,日本央行在为期两天的货币政策会议上,决定调整部分收益率曲线控制(YCC)政策,将长期利率波动幅度从原
12月FOMC会议报告市场预期主要央行加息周期何时见顶?内容摘要美联储加息50bp,将联邦基金利率目标区间上调至4。254。5,同幅度上调超额准备金利率至4。4隔夜逆回购利率至4。3。对于2024年,有7名委员认为应降息100bp至44
永兴联通创新打造智慧社区助力社会治理数字化红网时刻新闻12月22日讯(通讯员何杨)日前,由永兴联通打造的智慧社区平台正式亮相于永兴金穗家园小区,通过数字赋能实现社区治理全域化网格管理精细化,智慧社区让服务居民更有温度,更有
新消费观察快递春节不打烊,物流企业纷纷推出年货消费保障计划封面新闻记者雷强随着春节临近,采购年货成了不少消费者的头等大事。春节不打烊已成为近年快递业的标配,目前已有多家快递企业联合电商平台开始为年货春运做准备。天猫和菜鸟12月21日宣布,
隔夜美股大指数均涨超1汽车板块小鹏涨近1112月21日美东时间周三,耐克与联邦快递两家大型公司的财报点燃了企业盈利状况可能好于预期的希望。市场仍在评估美联储政策前景与美国经济衰退风险,截止收盘,道指收盘上涨526。74点,
年度家电总结消费篇尽管2022年开启于冬奥会的欢快气氛中,但冬奥会结束后,全球便迎来俄乌冲突黑天鹅,原本已经处于回落区间的原材料价格再度飙升,欧美地区的通胀水平进一步拉高,美联储紧接着启动加息,将全
2023年就业重点活动有哪些?请查收央视网消息中央经济工作会议强调,对于我们这么大的经济体而言,保持经济平稳运行至关重要。要着力稳增长稳就业稳物价,保持经济运行在合理区间。该如何稳就业?13月,人力资源社会保障部组织
什么是富硒农产品?缺硒人群如何选择富硒农产品?随着生活习惯和饮食习惯的改变,如今越来越多的人成为亚健康人群,主要表现有怕冷怕热易于感冒骨质疏松睡眠紊乱等。造成亚健康状态的原因有很多,总结起来就是内因和外因,内因主要是受情绪影响
山东深化预算绩效管理改革2019年以来,山东省深化财政预算绩效管理改革,进行事前事中事后全过程绩效评价。通过对项目预算进行成本分析测算和控制,强化监督考核,试点地区把钱花在刀刃上,有效化解了财政收支矛盾,
冯杰争分夺秒履行与自己的十年之约工人日报中工网记者邹明强通讯员王嵩松李晴14年前,河南农村小伙冯杰,一脸的稚气入职江汉石油工程公司。形单影只的青葱少年,望向一台台正在驶入基地的压裂车组,他却暗自下定决心,未来一定