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

前端缓存策略最佳实践

探讨了前端缓存策略的重要性及实施方法。内容涵盖缓存对性能、用户体验及带宽节省的作用,分析了滥用 localStorage 等常见误区。重点介绍了构建完整缓存管理系统的方法,包括 CacheManager 类的过期策略与空间管理,以及 Service Worker 在静态资源缓存中的应用。同时总结了 localStorage、sessionStorage、IndexedDB 与 Service Worker 的适用场景,强调合理设置过期时间、统一命名规范及错误处理,以实现高效的缓存优化。

漫步发布于 2026/4/6更新于 2026/10/24100 浏览

前端缓存策略最佳实践

为什么需要缓存策略

  • 性能提升:减少重复请求,加快页面加载速度
  • 用户体验:离线访问,减少等待时间
  • 带宽节省:减少服务器流量,降低成本
  • 可靠性:网络不稳定时仍能正常访问

常见误区与反面案例

// 反面教材:滥用 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) {
    // 缓存键没有统一规范
    .(, .(data));
}
localStorage
setItem
`cache_${key}_${Date.now()}`
JSON
stringify

推荐实现方案

// 正确的做法:完整的缓存策略
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;
    }
}

// 正确的做法:使用 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;
                    });
            })
    );
});

// 正确的做法: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();
    // 缓存数据
    cacheManager.set(cacheKey, data, 5 * 60 * 1000); // 5 分钟过期
    return data;
}

总结

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

目录

  1. 前端缓存策略最佳实践
  2. 为什么需要缓存策略
  3. 常见误区与反面案例
  4. 推荐实现方案
  5. 总结

更多推荐文章

查看全部
  • API、REST API、RESTful API 与 Web Service 的区别
  • Prometheus 集成 Python 业务指标的三种主流方案及代码示例
  • 循环神经网络(RNN)与序列数据处理实战
  • Trae AI 与 VS Code 配置 Claude、GPT、Gemini 大模型接入教程
  • Llama.cpp 部署教程:老旧电脑运行大模型方案
  • MySQL 数据库基础:概念、架构与核心使用指南
  • 网络安全行业前景、求职方向与学习路线
  • Xilinx Ultrascale+ FPGA 中 XDMA 时序约束配置指南
  • 医疗 AI 多智能体协同路径规划技术综述(下)
  • 智能家居多协议网关融合配置技术解析
  • AI 短视频制作技术指南:文本、图片与视频生成
  • 大语言模型(LLM)核心概念与架构解析
  • 前端错误处理最佳实践:构建健壮的用户体验
  • macOS 上安装 notepad-- 文本编辑器的 5 种方法
  • 11 款常用 C++ 在线编译与运行平台推荐与对比
  • PHP 低代码流程建模的 5 个致命误区与优化实践
  • Spring Cloud Sentinel 熔断降级核心原理与实战指南
  • C++ Vector 容器详解:基本用法与迭代器失效
  • 国内主流大模型 API 调用指南与 Python 实战
  • WorkBuddy 接入 QQ 机器人配置指南

相关免费在线工具

  • 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

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online