南平市护送信息网

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

2026-04-05 06:44:02 浏览次数:0
详细信息

前端缓存方式对比表

缓存方案对比表

缓存类型 存储位置 容量限制 生命周期 是否与服务端交互 适用场景
HTTP缓存 浏览器 根据浏览器和磁盘空间 可配置(Expires/Cache-Control) 静态资源缓存、API响应缓存
Cookie 浏览器 4KB左右 可设置过期时间 每次请求自动携带 会话管理、用户标识
LocalStorage 浏览器 5-10MB 永久存储,除非手动清除 用户偏好设置、应用状态持久化
SessionStorage 浏览器 5-10MB 标签页/会话期间 表单数据临时保存、单次会话状态
IndexedDB 浏览器 无明确限制(通常50MB+) 永久存储 结构化数据、离线应用数据存储
Service Worker缓存 浏览器/Service Worker 动态分配(通常50MB+) 可编程控制 可配置 离线应用、资源预加载、网络降级处理

Service Worker缓存详细讲解

一、Service Worker概述

Service Worker 是一种在浏览器后台运行的脚本,它独立于网页,可以拦截和处理网络请求,实现强大的缓存控制和离线功能。

二、生命周期

// 注册Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(registration => {
      console.log('SW注册成功:', registration.scope);
    })
    .catch(error => {
      console.log('SW注册失败:', error);
    });
}

生命周期阶段:

注册安装激活运行闲置终止

三、缓存策略实现

1. 基本缓存结构
// sw.js - Service Worker文件
const CACHE_NAME = 'app-cache-v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles/main.css',
  '/scripts/app.js',
  '/images/logo.png'
];

// 安装阶段 - 预缓存关键资源
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('缓存已打开');
        return cache.addAll(STATIC_ASSETS);
      })
      .then(() => self.skipWaiting()) // 跳过等待,立即激活
  );
});

// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CACHE_NAME) {
            console.log('删除旧缓存:', cacheName);
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => self.clients.claim()) // 立即控制所有客户端
  );
});
2. 核心缓存策略
// 拦截和处理请求
self.addEventListener('fetch', event => {
  event.respondWith(
    // 策略选择器
    cacheStrategy(event.request)
      .catch(() => fallbackStrategy(event.request))
  );
});

// 缓存优先策略
async function cacheFirst(request) {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    return cachedResponse;
  }

  try {
    const networkResponse = await fetch(request);

    // 缓存新资源
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, networkResponse.clone());

    return networkResponse;
  } catch (error) {
    // 网络失败,返回兜底内容
    return new Response('网络连接失败,请检查网络设置', {
      status: 408,
      headers: { 'Content-Type': 'text/plain' }
    });
  }
}

// 网络优先策略
async function networkFirst(request) {
  try {
    const networkResponse = await fetch(request);

    // 更新缓存
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, networkResponse.clone());

    return networkResponse;
  } catch (error) {
    // 网络失败时尝试从缓存获取
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }
    throw error;
  }
}

// Stale-While-Revalidate策略(推荐)
async function staleWhileRevalidate(request) {
  const cache = await caches.open(CACHE_NAME);
  const cachedResponse = await cache.match(request);

  // 立即返回缓存(无论新旧)
  const fetchPromise = fetch(request).then(networkResponse => {
    // 更新缓存
    cache.put(request, networkResponse.clone());
    return networkResponse;
  });

  return cachedResponse || fetchPromise;
}

// 策略选择器
function cacheStrategy(request) {
  const url = new URL(request.url);

  // 根据请求类型选择策略
  if (request.method !== 'GET') {
    return fetch(request); // 非GET请求直接走网络
  }

  if (url.pathname.startsWith('/api/')) {
    // API请求使用网络优先
    return networkFirst(request);
  }

  if (url.pathname.includes('.html') || url.pathname === '/') {
    // HTML文件使用网络优先,确保内容最新
    return networkFirst(request);
  }

  if (url.pathname.includes('version=')) {
    // 带版本号的资源直接走网络(可能是新版本)
    return fetch(request);
  }

  // 静态资源使用Stale-While-Revalidate
  return staleWhileRevalidate(request);
}

// 兜底策略
async function fallbackStrategy(request) {
  const url = new URL(request.url);

  // 返回离线页面
  if (request.headers.get('Accept').includes('text/html')) {
    return caches.match('/offline.html');
  }

  // 返回默认图片
  if (request.headers.get('Accept').includes('image')) {
    return caches.match('/images/default-placeholder.png');
  }

  return new Response('资源不可用', {
    status: 503,
    headers: { 'Content-Type': 'text/plain' }
  });
}

四、高级功能

1. 消息通信
// Service Worker发送消息给页面
self.addEventListener('activate', event => {
  event.waitUntil(
    self.clients.matchAll().then(clients => {
      clients.forEach(client => {
        client.postMessage({
          type: 'SW_ACTIVATED',
          version: CACHE_NAME
        });
      });
    })
  );
});

// 接收页面消息
self.addEventListener('message', event => {
  if (event.data.type === 'GET_CACHE_INFO') {
    caches.open(CACHE_NAME).then(cache => {
      cache.keys().then(keys => {
        event.source.postMessage({
          type: 'CACHE_INFO',
          count: keys.length,
          size: '...'
        });
      });
    });
  }

  if (event.data.type === 'CLEAR_CACHE') {
    caches.delete(CACHE_NAME);
  }
});
2. 后台同步
// 注册后台同步
self.addEventListener('sync', event => {
  if (event.tag === 'sync-user-data') {
    event.waitUntil(syncUserData());
  }
});

async function syncUserData() {
  const requests = await getPendingRequests();
  for (const request of requests) {
    try {
      await fetch(request.url, request.options);
      await removePendingRequest(request.id);
    } catch (error) {
      console.error('同步失败:', error);
    }
  }
}
3. 推送通知
self.addEventListener('push', event => {
  const options = {
    body: event.data.text(),
    icon: '/images/icon-192.png',
    badge: '/images/badge-72.png',
    vibrate: [200, 100, 200],
    data: {
      url: '/notifications'
    }
  };

  event.waitUntil(
    self.registration.showNotification('新通知', options)
  );
});

self.addEventListener('notificationclick', event => {
  event.notification.close();

  event.waitUntil(
    clients.matchAll({ type: 'window' }).then(clientList => {
      // 如果已有窗口打开,聚焦它
      for (const client of clientList) {
        if (client.url === '/' && 'focus' in client) {
          return client.focus();
        }
      }
      // 否则打开新窗口
      if (clients.openWindow) {
        return clients.openWindow(event.notification.data.url);
      }
    })
  );
});

五、最佳实践

1. 版本控制
// 版本管理
const CACHE_VERSION = 'v2';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;

// 动态缓存版本检测
async function checkForUpdates() {
  const response = await fetch('/version-manifest.json');
  const manifest = await response.json();

  if (manifest.version !== CACHE_VERSION) {
    // 触发Service Worker更新
    navigator.serviceWorker.getRegistration().then(reg => {
      if (reg) reg.update();
    });
  }
}
2. 缓存容量管理
// 缓存清理和容量控制
async function manageCacheSize(maxSize = 50 * 1024 * 1024) { // 50MB
  const cache = await caches.open(CACHE_NAME);
  const keys = await cache.keys();

  let totalSize = 0;
  const entries = [];

  // 计算缓存大小
  for (const request of keys) {
    const response = await cache.match(request);
    if (response) {
      const contentLength = response.headers.get('content-length');
      const size = contentLength ? parseInt(contentLength) : 0;
      totalSize += size;
      entries.push({ request, size, timestamp: Date.now() });
    }
  }

  // 如果超过限制,清理最旧的缓存
  if (totalSize > maxSize) {
    entries.sort((a, b) => a.timestamp - b.timestamp);

    let sizeToDelete = 0;
    const toDelete = [];

    for (const entry of entries) {
      if (sizeToDelete >= maxSize * 0.2) break; // 删除20%的缓存
      toDelete.push(entry.request);
      sizeToDelete += entry.size;
    }

    await Promise.all(toDelete.map(req => cache.delete(req)));
  }
}
3. 性能监控
// 缓存命中率统计
const cacheStats = {
  hits: 0,
  misses: 0,
  errors: 0
};

// 增强的fetch事件处理
self.addEventListener('fetch', event => {
  const startTime = performance.now();

  event.respondWith(
    (async () => {
      try {
        const response = await cacheStrategy(event.request);

        // 统计信息
        if (response.headers.get('X-Cache-Status') === 'HIT') {
          cacheStats.hits++;
        } else {
          cacheStats.misses++;
        }

        // 添加性能监控头
        const headers = new Headers(response.headers);
        headers.set('X-Response-Time', `${Date.now() - startTime}ms`);

        return new Response(response.body, {
          status: response.status,
          statusText: response.statusText,
          headers: headers
        });
      } catch (error) {
        cacheStats.errors++;
        throw error;
      }
    })()
  );
});

六、调试和测试

// 调试工具
if (process.env.NODE_ENV === 'development') {
  // 开发环境特殊处理
  self.addEventListener('fetch', event => {
    const url = new URL(event.request.url);

    // 跳过开发服务器请求
    if (url.hostname === 'localhost' && url.port === '3000') {
      return;
    }

    // 正常的缓存处理
    event.respondWith(cacheStrategy(event.request));
  });
}

// Service Worker状态检查
function checkServiceWorkerStatus() {
  if (!navigator.serviceWorker) {
    console.log('当前浏览器不支持Service Worker');
    return false;
  }

  navigator.serviceWorker.getRegistration().then(registration => {
    if (registration) {
      console.log('Service Worker已注册:', registration.scope);
      registration.update(); // 强制更新
    } else {
      console.log('Service Worker未注册');
    }
  });

  return true;
}

七、注意事项

HTTPS要求:Service Worker 只能在 HTTPS 或 localhost 环境下运行 作用域限制:Service Worker 的作用域由其所在目录决定 缓存清理:用户可能手动清除缓存,需要做好错误处理 内存管理:长时间运行的Service Worker需要注意内存泄漏 更新策略:Service Worker 更新需要谨慎处理,避免破坏用户体验

八、适用场景总结

离线应用:如文档编辑器、阅读器 性能优化:静态资源缓存,减少网络请求 网络降级:弱网环境下的用户体验保障 推送通知:实现后台消息推送 后台同步:网络恢复后的数据同步

通过合理使用Service Worker缓存,可以显著提升Web应用的性能、可靠性和用户体验,特别是在移动网络环境下。

相关推荐