前端缓存方式 对比 和 Service Worker 缓存详解

前端缓存方式对比表

缓存方式 工作机制 优势 劣势 适用场景
HTTP缓存 通过HTTP头控制缓存策略 1. 浏览器自动管理 2. 减少网络请求 3. 强缓存零延迟 1. 需服务器配置 2. 缓存更新需策略控制 静态资源(CSS/JS/图片)
• 强缓存 (Expires/Cache-Control) 直接使用本地副本,不发请求 响应最快,零网络开销 更新不及时,需版本控制 版本化静态资源
• 协商缓存 (Last-Modified/Etag) 向服务器验证资源是否更新 保证资源最新 节省带宽 仍有请求延迟 需服务器计算标识 频繁更新的资源
Service Worker缓存 拦截请求,可编程控制缓存 1. 离线可用 2. 细粒度控制 3. 可结合多种策略 1. 实现复杂 2. 需HTTPS 3. 首次需注册 PWA、离线应用、复杂缓存策略
Web Storage (Local/Session Storage) 键值对存储,同源策略 1. 存储量大(5-10MB) 2. API简单 3. 同标签页共享 1. 同步操作 2. 仅字符串 3. 无自动过期 用户偏好、表单数据、非敏感信息
IndexedDB 浏览器中的NoSQL数据库 1. 大容量(>250MB) 2. 事务支持 3. 复杂查询 1. API复杂 2. 异步操作 3. 学习成本高 大型结构化数据、离线数据
Cookie 随请求自动发送到服务器 1. 自动管理 2. 服务端可读 3. 有过期机制 1. 容量小(4KB) 2. 性能开销 3. 安全性需注意 会话管理、身份认证
应用缓存 (Application Cache) 通过manifest文件声明缓存 1. 简单配置 2. 离线可用 1. 已废弃 2. 更新机制差 3. 调试困难 不推荐使用,用Service Worker替代

📊 缓存策略选择建议

按资源类型选择:

  • 静态资源(版本固定):强缓存 + 文件名哈希

  • 静态资源(频繁更新):协商缓存

  • API数据:Service Worker + 网络优先/缓存备用策略

  • 用户数据:Web Storage(小量)、IndexedDB(大量)

  • 会话数据:Session Storage / Cookie

性能优化组合:

  1. 静态资源Cache-Control: max-age=31536000(一年)+ 文件名哈希

  2. 动态内容Cache-Control: no-cache + ETag验证

  3. 离线应用:Service Worker(Cache API + 回退策略)

  4. 状态持久化:Redux/Pinia + Web Storage持久化插件

⚠️ 注意事项:

  • 缓存污染:合理设置缓存键,避免数据混淆

  • 内存管理:定期清理过期缓存(特别是Cache API)

  • 安全:敏感数据避免存储在LocalStorage

  • 版本控制:静态资源使用内容哈希,避免更新问题


🔄 缓存更新策略示例

javascript

javascript 复制代码
// Service Worker 缓存策略:网络优先,失败用缓存
self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
      .catch(() => caches.match(event.request))
  );
});

// 版本化缓存:资源URL带哈希
// style.abc123.css -> 强缓存一年
<link href="/css/style.abc123.css" rel="stylesheet">

选择合适的缓存策略需要综合考虑资源类型更新频率数据大小用户体验要求,通常需要组合使用多种缓存方式。


Service Worker 缓存详解


一、Service Worker 基础架构

1. 生命周期

复制代码
注册 → 安装 → 激活 → 监听事件 → 终止

2. 核心特性

  • 独立线程:与主线程分离,不阻塞UI

  • 可编程缓存:通过Cache API精细控制

  • 请求拦截:可拦截、修改网络请求

  • 离线支持:网络不可用时仍可响应

  • 后台同步:支持后台数据同步


二、Cache API 详解

1. 缓存存储结构

javascript 复制代码
// 打开/创建缓存
caches.open('v1').then(cache => {
  // cache 对象操作
});

// 查看所有缓存
caches.keys().then(keys => {
  console.log('所有缓存:', keys);
});

2. 缓存操作方法

javascript 复制代码
const CACHE_NAME = 'my-cache-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/app.js',
  '/images/logo.png'
];

// 1. 添加单个资源
cache.put(request, response);

// 2. 添加多个资源
cache.addAll(urlsToCache);

// 3. 匹配缓存
cache.match(request);

// 4. 删除缓存
cache.delete(request);

// 5. 获取所有缓存键
cache.keys();

三、常用缓存策略

1. 缓存优先(Cache First)

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 有缓存直接返回
        if (response) {
          return response;
        }
        // 否则从网络获取
        return fetch(event.request)
          .then(response => {
            // 可选:缓存新资源
            return caches.open(CACHE_NAME)
              .then(cache => {
                cache.put(event.request, response.clone());
                return response;
              });
          });
      })
  );
});

2. 网络优先(Network First)

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
      .then(response => {
        // 网络成功:更新缓存
        return caches.open(CACHE_NAME)
          .then(cache => {
            cache.put(event.request, response.clone());
            return response;
          });
      })
      .catch(() => {
        // 网络失败:使用缓存
        return caches.match(event.request);
      })
  );
});

3. 仅缓存(Cache Only)

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        if (!response) {
          // 没有缓存时返回兜底页面
          return caches.match('/offline.html');
        }
        return response;
      })
  );
});

4. 仅网络(Network Only)

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(fetch(event.request));
});

5. Stale-While-Revalidate(先旧后新)

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(CACHE_NAME).then(cache => {
      return cache.match(event.request).then(cachedResponse => {
        const fetchPromise = fetch(event.request).then(networkResponse => {
          // 更新缓存
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        // 优先返回缓存,同时更新缓存
        return cachedResponse || fetchPromise;
      });
    })
  );
});

6. 运行时缓存(Runtime Caching)

javascript 复制代码
// 动态决定缓存策略
self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  
  // API请求:网络优先
  if (url.pathname.startsWith('/api/')) {
    return networkFirstStrategy(event);
  }
  
  // 静态资源:缓存优先
  if (url.pathname.startsWith('/static/')) {
    return cacheFirstStrategy(event);
  }
  
  // 其他:默认策略
  return defaultStrategy(event);
});

四、高级缓存模式

1. 版本化缓存管理

javascript 复制代码
const CACHE_VERSION = 'v2';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
const OLD_CACHES = ['app-cache-v1'];

// 安装时:缓存关键资源
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(criticalResources))
      .then(() => self.skipWaiting()) // 立即激活
  );
});

// 激活时:清理旧缓存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (OLD_CACHES.includes(cacheName)) {
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => self.clients.claim()) // 控制所有客户端
  );
});

2. 缓存过期策略

javascript 复制代码
// 定期清理过期缓存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      return cache.keys().then(requests => {
        return Promise.all(
          requests.map(request => {
            return cache.match(request).then(response => {
              if (!response) return null;
              
              // 检查缓存时间
              const cachedDate = new Date(response.headers.get('date'));
              const now = new Date();
              const cacheAge = now - cachedDate;
              
              // 超过7天删除
              if (cacheAge > 7 * 24 * 60 * 60 * 1000) {
                return cache.delete(request);
              }
              return null;
            });
          })
        );
      });
    })
  );
});

3. 请求预缓存

javascript 复制代码
// 预缓存可能访问的资源
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => {
      // 缓存当前页面资源
      return cache.addAll(currentPageResources);
    }).then(() => {
      // 预加载其他页面资源(不阻塞安装)
      self.skipWaiting();
      return preloadFutureResources();
    })
  );
});

function preloadFutureResources() {
  return fetch('/api/predict-resources')
    .then(res => res.json())
    .then(resources => {
      return caches.open(CACHE_NAME)
        .then(cache => cache.addAll(resources));
    })
    .catch(err => console.log('预加载失败:', err));
}

4. 响应定制与降级

javascript 复制代码
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        if (response) return response;
        
        return fetch(event.request)
          .then(networkResponse => {
            // 只缓存成功的响应
            if (networkResponse.ok) {
              const clonedResponse = networkResponse.clone();
              caches.open(CACHE_NAME)
                .then(cache => cache.put(event.request, clonedResponse));
            }
            return networkResponse;
          })
          .catch(error => {
            // 根据请求类型返回不同降级内容
            const request = event.request;
            
            if (request.headers.get('Accept').includes('text/html')) {
              return caches.match('/offline.html');
            }
            
            if (request.headers.get('Accept').includes('image')) {
              return caches.match('/images/placeholder.jpg');
            }
            
            return new Response('网络不可用', {
              status: 503,
              headers: { 'Content-Type': 'text/plain' }
            });
          });
      })
  );
});

5. 智能缓存策略

javascript 复制代码
class SmartCache {
  constructor() {
    this.config = {
      maxSize: 50 * 1024 * 1024, // 50MB
      ttl: {
        html: 5 * 60 * 1000,     // 5分钟
        api: 60 * 1000,          // 1分钟
        image: 7 * 24 * 60 * 60 * 1000 // 7天
      }
    };
  }

  async shouldCache(request, response) {
    const url = new URL(request.url);
    
    // 不缓存非GET请求
    if (request.method !== 'GET') return false;
    
    // 不缓存API错误响应
    if (!response.ok) return false;
    
    // 不缓存大文件(>10MB)
    const contentLength = response.headers.get('content-length');
    if (contentLength > 10 * 1024 * 1024) return false;
    
    return true;
  }
}

五、性能优化技巧

1. 缓存分层策略

javascript 复制代码
// 多级缓存:内存 + Service Worker + HTTP
class LayeredCache {
  constructor() {
    this.cacheLevels = {
      memory: new Map(),      // 内存缓存(会话级)
      sw: CACHE_NAME,         // Service Worker缓存
      http: null              // HTTP缓存(浏览器控制)
    };
  }
  
  async get(request) {
    // 1. 先查内存缓存
    const memoryKey = request.url;
    if (this.cacheLevels.memory.has(memoryKey)) {
      return this.cacheLevels.memory.get(memoryKey);
    }
    
    // 2. 再查Service Worker缓存
    const swCache = await caches.open(this.cacheLevels.sw);
    const swResponse = await swCache.match(request);
    if (swResponse) {
      // 存入内存缓存
      this.cacheLevels.memory.set(memoryKey, swResponse.clone());
      return swResponse;
    }
    
    // 3. 最后走网络(HTTP缓存会生效)
    return fetch(request);
  }
}

2. 缓存预热

javascript 复制代码
// 空闲时预加载资源
self.addEventListener('message', event => {
  if (event.data.type === 'PRELOAD_RESOURCES') {
    event.waitUntil(
      preloadResources(event.data.urls)
    );
  }
});

function preloadResources(urls) {
  return Promise.all(
    urls.map(url => {
      return fetch(url)
        .then(response => {
          if (response.ok) {
            return caches.open(CACHE_NAME)
              .then(cache => cache.put(url, response));
          }
        })
        .catch(() => {/* 静默失败 */});
    })
  );
}

六、调试与监控

1. 调试工具

javascript 复制代码
// Service Worker调试日志
self.addEventListener('fetch', event => {
  console.log(`[SW] Fetch: ${event.request.url}`);
  console.log(`[SW] Mode: ${event.request.mode}`);
  console.log(`[SW] Cache: ${caches.keys()}`);
});

2. 缓存状态监控

javascript 复制代码
// 监控缓存使用情况
async function getCacheStats() {
  const cache = await caches.open(CACHE_NAME);
  const requests = await cache.keys();
  
  let totalSize = 0;
  const stats = await Promise.all(
    requests.map(async request => {
      const response = await cache.match(request);
      const contentLength = response.headers.get('content-length');
      return {
        url: request.url,
        size: parseInt(contentLength || 0),
        type: response.headers.get('content-type')
      };
    })
  );
  
  totalSize = stats.reduce((sum, item) => sum + item.size, 0);
  
  return {
    totalItems: stats.length,
    totalSize,
    items: stats
  };
}

七、最佳实践

推荐做法

  1. 资源分类:不同资源类型使用不同策略

  2. 版本控制:每次更新使用新缓存名称

  3. 渐进增强:缓存作为性能优化,不是必需功能

  4. 监控告警:监控缓存命中率和错误率

  5. 容量控制:定期清理过期缓存

⚠️ 注意事项

  1. HTTPS要求:生产环境必须使用HTTPS

  2. 缓存污染:避免缓存个性化内容

  3. 内存泄漏:定期清理无用缓存

  4. 更新策略:明确缓存失效机制

  5. 回退方案:网络和缓存都失败时的处理


📊 策略选择指南

场景 推荐策略 理由
静态资源(版本化) 缓存优先 + 长期缓存 内容不变,性能最优
API数据(实时性高) 网络优先 保证数据最新
用户生成内容 Stale-While-Revalidate 平衡速度与新鲜度
离线应用 缓存优先 + 降级页面 保证基本功能可用
大文件(视频/图片) 按需缓存 + 容量控制 避免存储空间占用过大

Service Worker缓存是现代Web应用性能优化的核心工具,合理使用可以显著提升用户体验,特别是在弱网和离线场景下。

相关推荐
廋到被风吹走12 小时前
【数据库】【Redis】定位、优势、场景与持久化机制解析
数据库·redis·缓存
java1234_小锋20 小时前
说说Redis的内存淘汰策略?
数据库·redis·缓存
怀旧,1 天前
【Linux系统编程】13. Ext系列⽂件系统
android·linux·缓存
不穿格子的程序员1 天前
Redis篇5——Redis深度剖析:系统的“隐形杀手”——热Key与大Key问题
数据库·redis·缓存·热key·大key
暴富暴富暴富啦啦啦1 天前
Map 缓存和拿取
前端·javascript·缓存
在路上看风景1 天前
02. 缓存行
缓存
此生只爱蛋2 天前
【Redis】列表List类型
数据库·redis·缓存
菜鸟小九2 天前
redis实战(缓存)
数据库·redis·缓存