aurora blog
  • JS

    • 基本汇总
    • Date时间
    • Array方法
    • String 方法
    • Object方法
    • RegExp正则
    • Es6 新特性等
    • Event-Loop
    • Http and Https
    • for in和for of区别
    • Web Worker
    • Promise && async
    • 堆内存 & 栈内存
    • JS设计模式探索
    • npm & yarn
    • Fetch和Axios的区别
    • 学习及面试问题整理
    • URL 输入到页面加载过程
    • 跨域&nginx本地项目代理
  • FE框架

    • react

      • 基本用法
      • react生命周期
      • hooks 16.8版本后
      • Route原理
      • Redux源码解析
      • React16 Fiber
      • React-VirtualDOM
      • React事件委托机制
      • React性能优化
      • 状态管理方案对比
      • React 18新特性
    • vue

      • vue 基本用法
      • vue 生命周期
      • VirtualDOM
      • vue 事件委托
      • vue 架构
      • vue 状态管理
      • 问题等
    • React-Native

      • React-Native 基本用法
    • 微前端

      • 遇到的问题
    • 鸿蒙 harmony

      • harmony 基础
  • 构建工具

    • webpack

      • Webpack配置详解
      • Webpack的使用
      • Babel-polyfill
      • webpack面试题
    • vite

      • vite配置详解
      • vite面试题
    • rollup

      • Rollup配置详解
      • rollup面试题
    • 构建工具对比
  • 性能优化

    • 性能优化策略
    • 缓存策略
    • 性能优化面试题
  • 浏览器

    • 浏览器渲染原理
    • 浏览器缓存机制
    • 浏览器面试题
  • 工程化

    • 代码规范
    • 工程化面试题
  • 前端安全

    • XSS 攻击与防护
    • CSRF 攻击与防护
    • 前端安全面试题
  • 移动端开发

    • 移动端适配
    • 移动端开发面试题
  • 前端监控

    • 错误监控
    • 性能监控
    • 前端监控面试题
  • Typescript

    • Typescript详解
  • Servers

    • Nodejs
    • Nginx
  • Git命令

    • git常用规范
  • 数据库

    • mongodb
    • mongodb
  • Other

    • Jenkins自动化部署

缓存策略

  • 缓存概述
  • HTTP 缓存
    • 1. 强缓存
    • 2. 协商缓存
    • 3. 缓存策略
  • Service Worker 缓存
    • 1. 缓存策略
    • 2. 缓存版本管理
  • 应用层缓存
    • 1. 内存缓存
    • 2. LocalStorage 缓存
  • 缓存最佳实践
    • 1. 缓存策略选择
    • 2. 版本控制
    • 3. 缓存更新
  • 常见问题
    • 1. 缓存不生效
    • 2. 缓存更新不及时
    • 3. 缓存占用空间过大

缓存概述

缓存是提升性能的重要手段,主要包括:

  • 浏览器缓存:HTTP 缓存
  • Service Worker 缓存:离线缓存
  • 应用缓存:内存缓存、本地存储

HTTP 缓存

1. 强缓存

Cache-Control:

Cache-Control: max-age=3600
Cache-Control: public, max-age=3600
Cache-Control: private, max-age=3600
Cache-Control: no-cache
Cache-Control: no-store
Cache-Control: must-revalidate

常用指令:

  • max-age:缓存有效期(秒)
  • public:可以被任何缓存存储
  • private:只能被浏览器缓存
  • no-cache:需要验证缓存
  • no-store:不缓存
  • must-revalidate:过期后必须验证

Expires:

Expires: Wed, 21 Oct 2025 07:28:00 GMT

优先级: Cache-Control > Expires

2. 协商缓存

ETag:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

Last-Modified:

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

优先级: ETag > Last-Modified

3. 缓存策略

静态资源(长期缓存):

Cache-Control: public, max-age=31536000, immutable

HTML(不缓存或短缓存):

Cache-Control: no-cache
# 或
Cache-Control: max-age=0, must-revalidate

API 响应(短缓存):

Cache-Control: private, max-age=300

Service Worker 缓存

1. 缓存策略

Cache First(缓存优先):

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request).then(response => {
        const responseClone = response.clone();
        caches.open('v1').then(cache => {
          cache.put(event.request, responseClone);
        });
        return response;
      });
    })
  );
});

Network First(网络优先):

self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request).catch(() => {
      return caches.match(event.request);
    })
  );
});

Stale While Revalidate(先返回缓存,后台更新):

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open('v1').then(cache => {
      return cache.match(event.request).then(response => {
        const fetchPromise = fetch(event.request).then(networkResponse => {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        return response || fetchPromise;
      });
    })
  );
});

2. 缓存版本管理

const CACHE_NAME = 'app-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/script/main.js',
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      return cache.addAll(urlsToCache);
    })
  );
});

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CACHE_NAME) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

应用层缓存

1. 内存缓存

class MemoryCache {
  constructor(maxSize = 100) {
    this.cache = new Map();
    this.maxSize = maxSize;
  }
  
  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;
  }
  
  set(key, value, ttl) {
    if (this.cache.size >= this.maxSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    
    const item = {
      value,
      expiry: ttl ? Date.now() + ttl : null,
    };
    
    this.cache.set(key, item);
  }
  
  clear() {
    this.cache.clear();
  }
}

2. LocalStorage 缓存

class StorageCache {
  constructor(prefix = 'cache_') {
    this.prefix = prefix;
  }
  
  get(key) {
    try {
      const item = localStorage.getItem(this.prefix + key);
      if (!item) return null;
      
      const { value, expiry } = JSON.parse(item);
      
      if (expiry && Date.now() > expiry) {
        localStorage.removeItem(this.prefix + key);
        return null;
      }
      
      return value;
    } catch (e) {
      return null;
    }
  }
  
  set(key, value, ttl) {
    try {
      const item = {
        value,
        expiry: ttl ? Date.now() + ttl : null,
      };
      localStorage.setItem(this.prefix + key, JSON.stringify(item));
    } catch (e) {
      // 存储空间不足
      console.error('Storage cache failed:', e);
    }
  }
  
  remove(key) {
    localStorage.removeItem(this.prefix + key);
  }
  
  clear() {
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith(this.prefix)) {
        localStorage.removeItem(key);
      }
    });
  }
}

缓存最佳实践

1. 缓存策略选择

资源类型缓存策略Cache-Control
HTML不缓存或短缓存no-cache 或 max-age=0
CSS/JS长期缓存 + 版本号max-age=31536000, immutable
图片长期缓存max-age=31536000
API短缓存max-age=300
字体长期缓存max-age=31536000

2. 版本控制

文件名版本号:

// webpack
output: {
  filename: '[name].[contenthash:8].js',
}

// vite
build: {
  rollupOptions: {
    output: {
      entryFileNames: 'js/[name]-[hash].js',
    },
  },
}

查询参数版本号:

<link rel="stylesheet" href="style.css?v=1.0.0" />

3. 缓存更新

强制更新:

// 添加版本号或时间戳
const url = `/api/data?v=${Date.now()}`;

Service Worker 更新:

// 检查更新
navigator.serviceWorker.register('/sw.js').then(registration => {
  registration.addEventListener('updatefound', () => {
    const newWorker = registration.installing;
    newWorker.addEventListener('statechange', () => {
      if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
        // 新版本可用,提示用户刷新
      }
    });
  });
});

常见问题

1. 缓存不生效

原因:

  • Cache-Control 配置错误
  • 文件名没有版本号
  • 浏览器禁用缓存

解决:

  • 检查 HTTP 响应头
  • 使用版本号或 hash
  • 清除浏览器缓存测试

2. 缓存更新不及时

原因:

  • 强缓存时间过长
  • 没有版本控制

解决:

  • 缩短缓存时间
  • 使用版本号或 hash
  • 使用协商缓存

3. 缓存占用空间过大

解决:

  • 限制缓存大小
  • 定期清理过期缓存
  • 使用 LRU 策略
最近更新:: 2025/11/20 14:50
Contributors: liyulai
Prev
性能优化策略
Next
性能优化面试题