像使用 Redis 一样操作 LocalStorage

参考 Redis 缓存接口封装 LocalStorage,在全栈项目中保持一致的缓存操作体验,降低前端同学转向全栈开发的心智负担

1. 为什么需要 Redis 风格的 LocalStorage 封装

在全栈开发中,后端通常使用 Redis 进行缓存管理,而前端则使用 LocalStorage 存储本地数据。两者的 API 接口差异较大,这给从前端转向全栈的开发者带来了额外的学习成本。

通过封装一个 Redis 风格的 LocalStorage 工具类,我们可以:

  • 保持前后端缓存操作接口一致
  • 提供更丰富的缓存管理功能(如过期时间、键匹配等)
  • 增强代码的可维护性和可读性
  • 为未来可能的后端迁移做好准备

2. 核心功能实现与代码解析

完整工具类实现

ts 复制代码
export abstract class CacheUtil {
  /**
   * 设置缓存
   * @param key 缓存键
   * @param value 缓存值
   * @param ttl 过期时间(单位:秒),-1 表示永不过期
   */
  static set(key: string, value: any, ttl: number = -1) {
    const data = { value, ttl: ttl === -1 ? ttl : Date.now() + ttl * 1000 }
    localStorage.setItem(key, JSON.stringify(data))
  }

  /**
   * 获取缓存
   * @param key 缓存键
   * @param defaultValue 缓存不存在或过期时的默认值
   * @returns 缓存值或默认值
   */
  static get<T = any>(key: string, defaultValue: T | null = null): T | null {
    try {
      const jsonStr = localStorage.getItem(key)
      if (!jsonStr) return defaultValue
      const data = JSON.parse(jsonStr)
      if (data.ttl === -1 || Date.now() <= data.ttl) return data.value
      localStorage.removeItem(key)
      return defaultValue
    } catch (error: unknown) {
      localStorage.removeItem(key)
      return defaultValue
    }
  }

  /**
   * 获取缓存剩余过期时间(秒)
   * -1 = 永久有效
   * -2 = 已过期/不存在
   */
  static ttl(key: string): number {
    try {
      const item = localStorage.getItem(key)
      if (!item) return -2
      const data = JSON.parse(item)
      if (data.ttl === -1) return -1
      const remaining = data.ttl - Date.now()
      return remaining > 0 ? Math.floor(remaining / 1000) : -2
    } catch {
      return -2 // 解析失败,视为无效缓存
    }
  }

  /**
   * 动态设置缓存过期时间
   * @param key 缓存键
   * @param ttl 过期时间(秒)
   * @returns 是否设置成功
   */
  static expire(key: string, ttl: number): boolean {
    const value = this.get(key)
    if (value === null) return false
    this.set(key, value, ttl)
    return true
  }

  /**
   * 删除缓存
   * @param key 缓存键
   */
  static del(key: string) {
    localStorage.removeItem(key)
  }

  /**
   * 清空所有缓存
   */
  static flushall() {
    localStorage.clear()
  }

  /**
   * 查找缓存键(支持通配符 *,和 Redis 用法一致)
   * @param pattern 匹配规则,例如 user*、*info、*token*,默认 *
   * @returns 匹配的键数组
   */
  static keys(pattern: string = '*'): string[] {
    const allKeys = Object.keys(localStorage)
    const regex = new RegExp(pattern.replace(/\*/g, '.*'))
    return allKeys.filter((key) => regex.test(key))
  }

  /**
   * 检查缓存是否存在且未过期
   * @param key 缓存键
   * @returns 是否存在有效缓存
   */
  static exists(key: string): boolean {
    return this.get(key) !== null
  }
}

核心设计要点

  1. 数据结构设计 :使用 { value, ttl } 结构存储缓存数据,其中 ttl 为过期时间戳或 -1(永不过期)

  2. 过期时间处理

    • 设置时计算绝对过期时间戳
    • 获取时检查是否过期,过期则自动清理
    • 提供 ttl 方法查看剩余过期时间
  3. 错误处理:通过 try-catch 捕获 JSON 解析异常,确保缓存操作的稳定性

  4. Redis 风格 API :实现了与 Redis 相似的 setgetdelexpirekeysexists 等方法

  5. 通配符支持keys 方法支持 * 通配符匹配,与 Redis 用法一致

3. 完整 API 接口说明

方法 功能描述 参数说明 返回值
set(key, value, ttl) 设置缓存 key: 缓存键 value: 缓存值 ttl: 过期时间(秒),默认 -1
get(key, defaultValue) 获取缓存 key: 缓存键 defaultValue: 默认值,默认 null 缓存值或默认值
ttl(key) 获取剩余过期时间 key: 缓存键 -1: 永久有效 -2: 已过期/不存在 正数: 剩余秒数
expire(key, ttl) 设置过期时间 key: 缓存键 ttl: 过期时间(秒) 是否设置成功
del(key) 删除缓存 key: 缓存键
flushall() 清空所有缓存
keys(pattern) 查找匹配的键 pattern: 匹配规则,默认 * 匹配的键数组
exists(key) 检查缓存是否存在 key: 缓存键 是否存在有效缓存

4. 实战使用示例

基础操作

ts 复制代码
// 设置缓存,1小时过期
CacheUtil.set('USER', { id: 1, name: 'John' }, 3600)

// 获取缓存
const user = CacheUtil.get('USER')
console.log(user) // { id: 1, name: 'John' }

过期时间管理

ts 复制代码
// 续期缓存,设置为2小时过期
CacheUtil.expire('USER', 7200)

// 查看剩余过期时间
const remainingTime = CacheUtil.ttl('USER')
console.log(`剩余过期时间:${remainingTime}秒`)

键管理

ts 复制代码
// 通配符查找键
const userKeys = CacheUtil.keys('USER*')
const infoKeys = CacheUtil.keys('*INFO')
console.log('用户相关键:', userKeys)
console.log('信息相关键:', infoKeys)

// 检查缓存是否存在
const exists = CacheUtil.exists('USER')
console.log('USER 缓存存在:', exists)

删除操作

ts 复制代码
// 删除指定缓存
CacheUtil.del('USER')

// 清空所有缓存
CacheUtil.flushall()

5. 性能考量与最佳实践

性能考量

  1. 存储限制:LocalStorage 通常有 5MB 左右的存储限制,避免存储过大的数据

  2. 读取性能:频繁读取大对象会影响性能,建议将数据合理拆分

  3. 过期检查 :每次 get 操作都会检查过期时间,对性能影响较小但需注意

  4. JSON 序列化:复杂对象的序列化/反序列化会有性能开销,建议存储结构尽量简单

最佳实践

  1. 命名规范 :使用统一的命名前缀(如 APP_)避免键名冲突

  2. 数据类型:只存储必要的数据,避免存储整个应用状态

  3. 过期策略:为临时数据设置合理的过期时间,避免占用存储空间

  4. 错误处理:虽然工具类已做了错误处理,但调用时仍需考虑异常情况

  5. 安全注意:不要存储敏感信息(如密码、Token)到 LocalStorage


你在项目中是如何管理本地缓存的?有哪些好用的缓存策略或工具推荐?欢迎在评论区分享你的经验和想法!

相关推荐
RONIN2 小时前
UI组件库elementplus
前端
汤姆Tom2 小时前
从 0 到 1 开发项目?你是否也是这样开始?先有再优化一步一步带你了解架构设计
前端·后端·架构
review445432 小时前
基于 Cursor 实现智能代码审查skill
前端
muskk6 小时前
一个文件,9万星:Karpathy 用 4 条规则治好了 AI 写代码的"坏毛病"
前端·后端
前端DOM哥6 小时前
8 年前的老代码 + 20 刀 AI token = 我的第一款独立产品
前端·人工智能·架构
宁雨桥6 小时前
深入理解 async/await的原理
前端·javascript·es8
编程自留地6 小时前
项目sql语句
前端·数据库·sql
WaywardOne7 小时前
一.iOS Objective-C Runtime 原理
前端·ios
心连欣7 小时前
轮播图实战:用Web API玩转元素属性操作
前端·javascript·api