JavaScript 实现支持过期时间的数据缓存功能
JavaScript 实现支持过期时间的数据缓存功能
要在 JavaScript 中实现数据缓存功能并支持设置过期时间,可以使用 localStorage
、sessionStorage
或内存对象(如 Map
或普通对象)来存储数据,并为每个缓存项设置一个过期时间。以下是一个简单的实现示例:
JavaScript 实现支持过期时间的数据缓存功能
1. 使用 localStorage
实现持久缓存
const Cache = { /** * 设置缓存 * @param {string} key - 缓存键 * @param {*} value - 缓存值 * @param {number} ttl - 缓存时间(毫秒) */ set(key, value, ttl) { const data = { value, expiry: ttl ? Date.now() + ttl : null, // 计算过期时间 }; localStorage.setItem(key, JSON.stringify(data)); }, /** * 获取缓存 * @param {string} key - 缓存键 * @returns {*} 缓存值或 null(如果过期或不存在) */ get(key) { const data = localStorage.getItem(key); if (!data) return null; try { const { value, expiry } = JSON.parse(data); if (expiry && Date.now() > expiry) { localStorage.removeItem(key); // 过期删除缓存 return null; } return value; } catch (e) { console.warn("缓存数据解析失败", e); return null; } }, /** * 删除缓存 * @param {string} key - 缓存键 */ remove(key) { localStorage.removeItem(key); }, /** * 清空所有缓存 */ clear() { localStorage.clear(); }, }; // 使用示例 Cache.set("username", "Alice", 5000); // 设置缓存5秒后过期 console.log(Cache.get("username")); // 5秒内返回 'Alice' setTimeout(() => console.log(Cache.get("username")), 6000); // 6秒后返回 null
⚠️ 注意事项
localStorage
只能存储字符串,因此要使用JSON.stringify
和JSON.parse
进行序列化和反序列化。localStorage
的存储空间一般有限(大约 5MB)。- 如果是跨页面使用,请确保在相同的域名下。
2. 使用 sessionStorage
实现数据缓存(适合页面级临时存储)
sessionStorage
是一种浏览器存储机制,它的特点是数据仅在页面会话(session) 期间有效,页面关闭后数据会被清除。
const SessionCache = { /** * 设置缓存 * @param {string} key - 缓存键 * @param {*} value - 缓存值 * @param {number} ttl - 缓存时间(毫秒) */ set(key, value, ttl) { const data = { value, expiry: ttl ? Date.now() + ttl : null, // 计算过期时间 }; sessionStorage.setItem(key, JSON.stringify(data)); }, /** * 获取缓存 * @param {string} key - 缓存键 * @returns {*} 缓存值或 null(如果过期或不存在) */ get(key) { const data = sessionStorage.getItem(key); if (!data) return null; try { const { value, expiry } = JSON.parse(data); if (expiry && Date.now() > expiry) { sessionStorage.removeItem(key); // 缓存已过期,删除 return null; } return value; } catch (e) { console.warn("缓存数据解析失败:", e); return null; } }, /** * 删除缓存 * @param {string} key - 缓存键 */ remove(key) { sessionStorage.removeItem(key); }, /** * 清空所有缓存 */ clear() { sessionStorage.clear(); }, }; // **使用示例** // 设置缓存,5秒后过期 SessionCache.set("userToken", "abc123", 5000); // 获取缓存 console.log(SessionCache.get("userToken")); // 5秒内返回 'abc123' // 5秒后尝试获取缓存 setTimeout(() => { console.log(SessionCache.get("userToken")); // 返回 null }, 6000);
⚠️ 注意事项
sessionStorage
数据在页面关闭或会话结束时自动清除。- 在不同的标签页中,
sessionStorage
是独立的(不会共享)。 sessionStorage
的存储空间一般为5MB左右。- 数据存储在
sessionStorage
时必须经过JSON.stringify
和JSON.parse
处理。
3. 使用内存对象实现轻量缓存(适合短期缓存)
class MemoryCache { constructor() { this.cache = new Map(); } /** * 设置缓存 * @param {string} key - 缓存键 * @param {*} value - 缓存值 * @param {number} ttl - 缓存时间(毫秒) */ set(key, value, ttl) { const expiry = ttl ? Date.now() + ttl : null; this.cache.set(key, { value, expiry }); } /** * 获取缓存 * @param {string} key - 缓存键 * @returns {*} 缓存值或 null(如果过期或不存在) */ get(key) { const item = this.cache.get(key); if (!item) return null; if (item.expiry && Date.now() > item.expiry) { this.cache.delete(key); // 缓存过期,删除 return null; } return item.value; } /** * 删除缓存 * @param {string} key - 缓存键 */ remove(key) { this.cache.delete(key); } /** * 清空所有缓存 */ clear() { this.cache.clear(); } } // 使用示例 const memCache = new MemoryCache(); memCache.set("token", "abc123", 3000); // 设置缓存3秒后过期 console.log(memCache.get("token")); // 3秒内返回 'abc123' setTimeout(() => console.log(memCache.get("token")), 4000); // 4秒后返回 null
4. 使用 IndexedDB 实现持久缓存(支持二进制数据)
// 初始化 IndexedDB 数据库 class IndexedDBCache { constructor(dbName = "CacheDB", storeName = "CacheStore") { this.dbName = dbName; this.storeName = storeName; this.db = null; } /** * 初始化数据库 */ async init() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, 1); request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName, { keyPath: "key" }); } }; request.onsuccess = (event) => { this.db = event.target.result; resolve(this); }; request.onerror = (event) => { console.error("IndexedDB 初始化失败:", event.target.error); reject(event.target.error); }; }); } /** * 获取对象存储 */ getObjectStore(mode = "readonly") { const transaction = this.db.transaction(this.storeName, mode); return transaction.objectStore(this.storeName); } /** * 添加或更新缓存 * @param {string} key - 缓存键 * @param {*} value - 缓存值 * @param {number} ttl - 过期时间(毫秒) */ async set(key, value, ttl) { const expiry = ttl ? Date.now() + ttl : null; const record = { key, value, expiry }; return new Promise((resolve, reject) => { const store = this.getObjectStore("readwrite"); const request = store.put(record); request.onsuccess = () => resolve(true); request.onerror = (event) => { console.error("数据写入失败:", event.target.error); reject(event.target.error); }; }); } /** * 获取缓存 * @param {string} key - 缓存键 * @returns {*} 缓存值或 null */ async get(key) { return new Promise((resolve, reject) => { const store = this.getObjectStore("readonly"); const request = store.get(key); request.onsuccess = (event) => { const record = event.target.result; if (!record) { resolve(null); // 数据不存在 return; } if (record.expiry && Date.now() > record.expiry) { // 数据过期 this.delete(key).then(() => resolve(null)); } else { resolve(record.value); // 返回未过期数据 } }; request.onerror = (event) => { console.error("数据读取失败:", event.target.error); reject(event.target.error); }; }); } /** * 删除缓存 * @param {string} key - 缓存键 */ async delete(key) { return new Promise((resolve, reject) => { const store = this.getObjectStore("readwrite"); const request = store.delete(key); request.onsuccess = () => resolve(true); request.onerror = (event) => { console.error("数据删除失败:", event.target.error); reject(event.target.error); }; }); } /** * 清空缓存 */ async clear() { return new Promise((resolve, reject) => { const store = this.getObjectStore("readwrite"); const request = store.clear(); request.onsuccess = () => resolve(true); request.onerror = (event) => { console.error("缓存清空失败:", event.target.error); reject(event.target.error); }; }); } } // 使用示例 (async () => { // 创建缓存实例并初始化数据库 const cache = new IndexedDBCache(); await cache.init(); // 设置缓存,数据 5 秒后过期 await cache.set("userToken", "abc123", 5000); console.log("缓存设置完成"); // 缓存二进制数据 const binaryData = new Blob(["Hello, IndexedDB"], { type: "text/plain" }); await cache.set("binary", binaryData, 5000); // 获取缓存 console.log("立即获取:", await cache.get("userToken")); // 输出: abc123 console.log("立即获取:", await cache.get("binary")); // 等待 6 秒后尝试获取缓存 setTimeout(async () => { console.log("6 秒后获取:", await cache.get("userToken")); // 输出: null console.log("6 秒后获取:", await cache.get("binary")); // 输出: null }, 6000); })();
🔑 功能说明
-
set(key, value, ttl)
- 添加或更新缓存。
ttl
(毫秒):设置缓存的过期时间,null
表示永久有效。
-
get(key)
- 读取缓存时会检查过期时间。
- 如果数据已过期,会自动删除并返回
null
。
-
delete(key)
- 删除指定的缓存。
-
clear()
- 清空所有缓存。
-
异步操作
- 所有方法基于
Promise
,方便与现代异步代码(async/await
)结合使用。
- 所有方法基于
🌟 优点
- 支持过期时间:在获取数据时会自动检查并清除过期数据。
- 高效查询:利用
IndexedDB
的键值对存储,快速读写数据。 - 持久化存储:数据保存在用户设备上,关闭浏览器后依然存在。
⚠️ 注意事项
- 浏览器兼容性:确保目标用户的浏览器支持
IndexedDB
(现代浏览器普遍支持)。 - 性能优化:如果有大量过期数据,可以定期批量清理(如通过后台任务)。
- 容量限制:大多数浏览器允许
IndexedDB
使用数百 MB 的存储空间,但仍需谨慎使用以避免存储满溢。
四种方式的对比
特性/方式 | localStorage | sessionStorage | IndexedDB | 内存缓存 (Map) |
---|---|---|---|---|
持久化能力 | 长期持久化,直到手动清除 | 会话级存储,页面关闭即清除 | 长期持久化,直到手动清除或删除 | 无持久化能力,页面刷新即丢失 |
存储容量 | 约 5MB | 约 5MB | 通常 >50MB,具体视浏览器实现而定 | 受内存限制,大小不固定 |
过期时间实现 | 需手动编码支持 | 需手动编码支持 | 原生支持复杂数据操作,需手动编码 | 直接使用定时器或清理机制实现 |
数据存取性能 | 高效,键值直接存取 | 高效,键值直接存取 | 高效,但比 localStorage 略慢 |
极高,内存操作无需异步 |
异步操作支持 | 不支持,操作是同步的 | 不支持,操作是同步的 | 原生异步,基于事件或 Promise | 不需要,直接操作内存 |
复杂数据支持 | 必须使用 JSON.stringify /parse |
必须使用 JSON.stringify /parse |
支持复杂对象、Blob、ArrayBuffer | 支持复杂对象,但无法序列化存储 |
跨页面共享 | 是 | 否 | 是 | 否 |
适合场景 | 跨页面长期数据存储(如用户设置) | 单页面会话数据存储(如表单数据) | 离线存储、大量复杂数据、文件缓存 | 短期临时数据、高性能场景 |
过期机制复杂度 | 中等(需要检查时间并清除过期数据) | 中等(需要检查时间并清除过期数据) | 较高(通过索引或批量检查清理) | 简单(可用定时器直接清除) |
💡 选择建议
场景 | 建议选择 | 理由 |
---|---|---|
短期缓存,高性能需求 | 内存缓存(Map) | 速度最快,适合临时存储。 |
会话级缓存,简单键值对 | sessionStorage | 页面关闭后自动清除,无需手动管理。 |
长期缓存,简单数据 | localStorage | 数据持久化存储,适合小型缓存。 |
长期缓存,复杂或大量数据 | IndexedDB | 可管理大数据量,支持复杂数据结构与索引。 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体