JavaScript 实现支持过期时间的数据缓存功能

JavaScript 实现支持过期时间的数据缓存功能

要在 JavaScript 中实现数据缓存功能并支持设置过期时间,可以使用 localStoragesessionStorage 或内存对象(如 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.stringifyJSON.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.stringifyJSON.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("缓存设置完成");

  // 获取缓存
  console.log("立即获取:", await cache.get("userToken")); // 输出: abc123

  // 等待 6 秒后尝试获取缓存
  setTimeout(async () => {
    console.log("6 秒后获取:", await cache.get("userToken")); // 输出: null
  }, 6000);
})();

功能说明

  1. set(key, value, ttl)

    • 添加或更新缓存。
    • ttl(毫秒):设置缓存的过期时间,null 表示永久有效。
  2. get(key)

    • 读取缓存时会检查过期时间。
    • 如果数据已过期,会自动删除并返回 null
  3. delete(key)

    • 删除指定的缓存。
  4. clear()

    • 清空所有缓存。
  5. 异步操作

    • 所有方法基于 Promise,方便与现代异步代码(async/await)结合使用。

优点

  1. 支持过期时间:在获取数据时会自动检查并清除过期数据。
  2. 高效查询:利用 IndexedDB 的键值对存储,快速读写数据。
  3. 持久化存储:数据保存在用户设备上,关闭浏览器后依然存在。

注意事项

  1. 浏览器兼容性:确保目标用户的浏览器支持 IndexedDB(现代浏览器普遍支持)。
  2. 性能优化:如果有大量过期数据,可以定期批量清理(如通过后台任务)。
  3. 容量限制:大多数浏览器允许 IndexedDB 使用数百 MB 的存储空间,但仍需谨慎使用以避免存储满溢。

四种方式的对比

特性/方式 localStorage sessionStorage IndexedDB 内存缓存 (Map)
持久化能力 长期持久化,直到手动清除 会话级存储,页面关闭即清除 长期持久化,直到手动清除或删除 无持久化能力,页面刷新即丢失
存储容量 约 5MB 约 5MB 通常 >50MB,具体视浏览器实现而定 受内存限制,大小不固定
过期时间实现 需手动编码支持 需手动编码支持 原生支持复杂数据操作,需手动编码 直接使用定时器或清理机制实现
数据存取性能 高效,键值直接存取 高效,键值直接存取 高效,但比 localStorage 略慢 极高,内存操作无需异步
异步操作支持 不支持,操作是同步的 不支持,操作是同步的 原生异步,基于事件或 Promise 不需要,直接操作内存
复杂数据支持 必须使用 JSON.stringify/parse 必须使用 JSON.stringify/parse 支持复杂对象、Blob、ArrayBuffer 支持复杂对象,但无法序列化存储
跨页面共享
适合场景 跨页面长期数据存储(如用户设置) 单页面会话数据存储(如表单数据) 离线存储、大量复杂数据、文件缓存 短期临时数据、高性能场景
过期机制复杂度 中等(需要检查时间并清除过期数据) 中等(需要检查时间并清除过期数据) 较高(通过索引或批量检查清理) 简单(可用定时器直接清除)

选择建议

场景 建议选择 理由
短期缓存,高性能需求 内存缓存(Map) 速度最快,适合临时存储。
会话级缓存,简单键值对 sessionStorage 页面关闭后自动清除,无需手动管理。
长期缓存,简单数据 localStorage 数据持久化存储,适合小型缓存。
长期缓存,复杂或大量数据 IndexedDB 可管理大数据量,支持复杂数据结构与索引。

来源链接:https://www.cnblogs.com/yuzhihui/p/18657004

© 版权声明
THE END
支持一下吧
点赞10 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码

    暂无评论内容