跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端算法

前端缓存策略实战:从 localStorage 到 Service Worker

前端缓存策略涉及多种存储方式的选择与应用。通过合理配置 localStorage、IndexedDB 及 Service Worker,可显著降低服务器负载并提升加载速度。文章剖析了滥用本地存储等常见误区,展示了包含过期机制与空间管理的完整缓存类实现。重点强调了统一命名规范、错误处理及定期清理的重要性,帮助开发者构建健壮的缓存系统。

字节跳动发布于 2026/4/7更新于 2026/9/963 浏览

前端缓存策略实战:从 localStorage 到 Service Worker

常见误区

很多开发者对缓存存在误解,认为这只是浏览器自动处理的事。比如直接禁用缓存省麻烦,结果每次都要重新加载,浪费带宽;或者以为缓存就是 localStorage,管理混乱导致内存占用高。醒醒吧,前端缓存不是简单的本地存储,而是一套完整的策略体系。

为什么需要它?

合理的缓存策略能带来实实在在的好处:减少重复请求以加快页面加载,支持离线访问提升用户体验,节省服务器流量降低成本,甚至在网络不稳定时保证应用可用性。

反面教材

看看这些典型的错误写法,它们往往会导致性能问题或数据不一致。

// 反面教材:滥用 localStorage,没有失效机制
function fetchData() {
  // 每次都从 API 获取数据
  return fetch('https://api.example.com/data')
    .then(res => res.json())
    .then(data => {
      // 直接存储到 localStorage,不考虑过期
      localStorage.setItem('data', JSON.stringify(data));
      return data;
    });
}

// 反面教材:永远使用缓存,不考虑过期
function getCachedData() {
  const cachedData = localStorage.getItem('data');
  return cachedData ? JSON.parse(cachedData) : null;
}

// 反面教材:缓存键命名混乱
function cacheData(key, data) {
  // 缓存键没有统一规范,难以维护
  localStorage.setItem(`cache_${key}_${Date.now()}`, JSON.stringify(data));
}

正确的做法

我们需要构建一个完整的缓存管理系统,包含过期策略、空间管理和统一的命名规范。

1. 封装 CacheManager

这个类负责处理数据的增删改查以及过期判断。

class CacheManager {
  constructor() {
    this.cachePrefix = 'app_cache_';
    this.defaultExpiry = 24 * 60 * 60 * 1000; // 默认 24 小时
  }

  generateKey(key) {
    return `${this.cachePrefix}${key}`;
  }

  set(key, data, expiry = this.defaultExpiry) {
    const cacheItem = {
      data,
      expiry: Date.now() + expiry,
      timestamp: Date.now()
    };
    try {
      localStorage.setItem(this.generateKey(key), JSON.stringify(cacheItem));
    } catch (error) {
      console.error('Cache storage error:', error);
      this.clearOldCache();
    }
  }

  get(key) {
    const cacheItem = localStorage.getItem(this.generateKey(key));
    if (!cacheItem) return null;
    try {
      const parsedItem = JSON.parse(cacheItem);
      // 检查是否过期
      if (Date.now() > parsedItem.expiry) {
        this.remove(key);
        return null;
      }
      return parsedItem.data;
    } catch (error) {
      console.error('Cache parsing error:', error);
      this.remove(key);
      return null;
    }
  }

  remove(key) {
    localStorage.removeItem(this.generateKey(key));
  }

  clear() {
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith(this.cachePrefix)) {
        localStorage.removeItem(key);
      }
    });
  }

  clearOldCache() {
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith(this.cachePrefix)) {
        try {
          const item = JSON.parse(localStorage.getItem(key));
          if (Date.now() > item.expiry) {
            localStorage.removeItem(key);
          }
        } catch (error) {
          localStorage.removeItem(key);
        }
      }
    });
  }

  getCacheSize() {
    let size = 0;
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith(this.cachePrefix)) {
        size += localStorage.getItem(key).length;
      }
    });
    return size;
  }
}
2. 使用 Service Worker 缓存静态资源

对于静态资源和 API 响应,Service Worker 是更强大的选择,它能拦截网络请求并控制缓存逻辑。

// service-worker.js
const CACHE_NAME = 'app-cache-v1';
const ASSETS_TO_CACHE = [
  '/',
  '/index.html',
  '/manifest.json',
  '/static/js/main.js',
  '/static/css/main.css',
  '/static/images/logo.png'
];

// 安装 Service Worker
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Opened cache');
        return cache.addAll(ASSETS_TO_CACHE);
      })
  );
});

// 激活 Service Worker
self.addEventListener('activate', event => {
  const cacheWhitelist = [CACHE_NAME];
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// 拦截网络请求
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 如果缓存中有响应,直接返回
        if (response) {
          return response;
        }
        // 否则发起网络请求
        return fetch(event.request)
          .then(response => {
            // 如果响应有效,缓存一份
            if (response && response.status === 200 && response.type === 'basic') {
              const responseToCache = response.clone();
              caches.open(CACHE_NAME)
                .then(cache => {
                  cache.put(event.request, responseToCache);
                });
            }
            return response;
          })
      )
  );
});
3. API 请求缓存封装

结合上面的管理器,我们可以轻松实现带缓存的 API 请求。

async function fetchWithCache(url, options = {}) {
  const cacheKey = `api_${url}_${JSON.stringify(options)}`;
  const cacheManager = new CacheManager();

  // 尝试从缓存获取
  const cachedData = cacheManager.get(cacheKey);
  if (cachedData) {
    return cachedData;
  }

  // 发起网络请求
  const response = await fetch(url, options);
  const data = await response.json();

  // 缓存数据,5 分钟过期
  cacheManager.set(cacheKey, data, 5 * 60 * 1000);
  return data;
}

核心建议

记住,缓存不是越多越好,而是要合理使用。你需要根据数据的性质和使用频率,选择合适的缓存策略。不要觉得缓存只是浏览器的事,它是前端性能优化的重要组成部分。

总结

  • localStorage:适合存储小量、不敏感的数据
  • sessionStorage:适合存储会话期间的数据
  • IndexedDB:适合存储大量结构化数据
  • Service Worker:适合缓存静态资源和 API 响应
  • 缓存策略:合理设置过期时间,定期清理过期缓存
  • 缓存键管理:使用统一的命名规范,避免缓存键冲突
  • 错误处理:处理存储空间不足等异常情况
  • 性能监控:监控缓存命中率,不断优化缓存策略

目录

  1. 前端缓存策略实战:从 localStorage 到 Service Worker
  2. 常见误区
  3. 为什么需要它?
  4. 反面教材
  5. 正确的做法
  6. 1. 封装 CacheManager
  7. 2. 使用 Service Worker 缓存静态资源
  8. 3. API 请求缓存封装
  9. 核心建议
  10. 总结

更多推荐文章

查看全部
  • 英文论文 AI 率检测:iThenticate 与 Turnitin 系统对比
  • C++微服务 UserServer 设计与实现
  • Llama.cpp 跨平台本地大模型部署实战指南
  • DeOldify 图像上色:黑白漫画转赛博朋克/水墨/油画风格
  • 基于 Rust 与 GLM-5 构建高性能 AI 翻译 CLI 工具
  • Spring AI Tool Calling 实战:自动执行、手动控制与异常处理
  • OpenClaw Web Search 工具配置与渠道详解
  • Django 配置 Swagger 文档的详细步骤与代码示例
  • 本地私有 AI 大模型智能体搭建实战
  • DSPy 技术详解:从 Prompt 工程到编程式大模型应用
  • OpenCode 集成 Kimi K2.5 配置与使用指南
  • MaxClaw:Go 语言实现的本地优先 AI 智能体平台
  • Python 列表内存存储本质:差异原因与优化建议
  • 使用 AI 工具快速生成专业架构图的几种方法
  • ComfyUI 节点式 AI 绘画工作流核心解析
  • icomet-server:基于 C++ 的高并发推送服务
  • DeepSeek-R1-Distill-Llama-8B 本地部署与 Docker Compose 服务化
  • GitHub 镜像加速:Gitee 与 Coding 开源项目克隆指南
  • GitHub Copilot 提升开发效率实战记录
  • 深入理解 Java 语言的核心概念

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online