郭根久 發表於 2025-12-30 09:57:44

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">前端缓存方式对比表</a></li><li><a href="#_label1">缓存策略选择建议</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">按资源类型选择:</a></li><li><a href="#_lab2_1_1">性能优化组合:</a></li><li><a href="#_lab2_1_2">注意事项:</a></li><li><a href="#_lab2_1_3">缓存更新策略示例</a></li></ul><li><a href="#_label2">Service Worker 缓存详解</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_4">一、Service Worker 基础架构</a></li><ul class="third_class_ul"><li><a href="#_label3_2_4_0">1.生命周期</a></li><li><a href="#_label3_2_4_1">2.核心特性</a></li></ul><li><a href="#_lab2_2_5">二、Cache API 详解</a></li><ul class="third_class_ul"><li><a href="#_label3_2_5_2">1.缓存存储结构</a></li><li><a href="#_label3_2_5_3">2.缓存操作方法</a></li></ul><li><a href="#_lab2_2_6">三、常用缓存策略</a></li><ul class="third_class_ul"><li><a href="#_label3_2_6_4">1.缓存优先(Cache First)</a></li><li><a href="#_label3_2_6_5">2.网络优先(Network First)</a></li><li><a href="#_label3_2_6_6">3.仅缓存(Cache Only)</a></li><li><a href="#_label3_2_6_7">4.仅网络(Network Only)</a></li><li><a href="#_label3_2_6_8">5.Stale-While-Revalidate(先旧后新)</a></li><li><a href="#_label3_2_6_9">6.运行时缓存(Runtime Caching)</a></li></ul><li><a href="#_lab2_2_7">四、高级缓存模式</a></li><ul class="third_class_ul"><li><a href="#_label3_2_7_10">1.版本化缓存管理</a></li><li><a href="#_label3_2_7_11">2.缓存过期策略</a></li><li><a href="#_label3_2_7_12">3.请求预缓存</a></li><li><a href="#_label3_2_7_13">4.响应定制与降级</a></li><li><a href="#_label3_2_7_14">5.智能缓存策略</a></li></ul><li><a href="#_lab2_2_8">五、性能优化技巧</a></li><ul class="third_class_ul"><li><a href="#_label3_2_8_15">1.缓存分层策略</a></li><li><a href="#_label3_2_8_16">2.缓存预热</a></li></ul><li><a href="#_lab2_2_9">六、调试与监控</a></li><ul class="third_class_ul"><li><a href="#_label3_2_9_17">1.调试工具</a></li><li><a href="#_label3_2_9_18">2.缓存状态监控</a></li></ul><li><a href="#_lab2_2_10">七、最佳实践</a></li><ul class="third_class_ul"><li><a href="#_label3_2_10_19">推荐做法</a></li><li><a href="#_label3_2_10_20">注意事项</a></li></ul><li><a href="#_lab2_2_11">策略选择指南</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label3">总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>前端缓存方式对比表</h2>
<table><thead><tr><th>缓存方式</th><th>工作机制</th><th>优势</th><th>劣势</th><th>适用场景</th></tr></thead><tbody><tr><td><span><strong>HTTP缓存</strong></span></td><td><span>通过HTTP头控制缓存策略</span></td><td><span>1. 浏览器自动管理<br />2. 减少网络请求<br />3. 强缓存零延迟</span></td><td><span>1. 需服务器配置<br />2. 缓存更新需策略控制</span></td><td><span>静态资源(CSS/JS/图片)</span></td></tr><tr><td>&bull; 强缓存<br />(Expires/Cache-Control)</td><td>直接使用本地副本,不发请求</td><td>响应最快,零网络开销</td><td>更新不及时,需版本控制</td><td>版本化静态资源</td></tr><tr><td>&bull; 协商缓存<br />(Last-Modified/Etag)</td><td>向服务器验证资源是否更新</td><td>保证资源最新<br />节省带宽</td><td>仍有请求延迟<br />需服务器计算标识</td><td>频繁更新的资源</td></tr><tr><td><span><strong>Service Worker缓存</strong></span></td><td><span>拦截请求,可编程控制缓存</span></td><td><span>1. 离线可用<br />2. 细粒度控制<br />3. 可结合多种策略</span></td><td><span>1. 实现复杂<br />2. 需HTTPS<br />3. 首次需注册</span></td><td><span>PWA、离线应用、复杂缓存策略</span></td></tr><tr><td><strong>Web Storage</strong><br />(Local/Session Storage)</td><td>键值对存储,同源策略</td><td>1. 存储量大(5-10MB)<br />2. API简单<br />3. 同标签页共享</td><td>1. 同步操作<br />2. 仅字符串<br />3. 无自动过期</td><td>用户偏好、表单数据、非敏感信息</td></tr><tr><td><strong>IndexedDB</strong></td><td>浏览器中的NoSQL数据库</td><td>1. 大容量(&gt;250MB)<br />2. 事务支持<br />3. 复杂查询</td><td>1. API复杂<br />2. 异步操作<br />3. 学习成本高</td><td>大型结构化数据、离线数据</td></tr><tr><td><strong>Cookie</strong></td><td>随请求自动发送到服务器</td><td>1. 自动管理<br />2. 服务端可读<br />3. 有过期机制</td><td>1. 容量小(4KB)<br />2. 性能开销<br />3. 安全性需注意</td><td>会话管理、身份认证</td></tr><tr><td><strong>应用缓存</strong><br />(Application Cache)</td><td>通过manifest文件声明缓存</td><td>1. 简单配置<br />2. 离线可用</td><td><span>1.&nbsp;<strong>已废弃</strong><br />2. 更新机制差<br />3. 调试困难</span></td><td><span>不推荐使用,用Service Worker替代</span></td></tr></tbody></table>
<p class="maodian"><a name="_label1"></a></p><h2>缓存策略选择建议</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>按资源类型选择:</h3>
<ul><li><p><strong>静态资源(版本固定)</strong>:强缓存 + 文件名哈希</p></li><li><p><strong>静态资源(频繁更新)</strong>:协商缓存</p></li><li><p><strong>API数据</strong>:Service Worker + 网络优先/缓存备用策略</p></li><li><p><strong>用户数据</strong>:Web Storage(小量)、IndexedDB(大量)</p></li><li><p><strong>会话数据</strong>:Session Storage / Cookie</p></li></ul>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>性能优化组合:</h3>
<ol><li><p><strong>静态资源</strong>:<code>Cache-Control: max-age=31536000</code>(一年)+ 文件名哈希</p></li><li><p><strong>动态内容</strong>:<code>Cache-Control: no-cache</code>&nbsp;+ ETag验证</p></li><li><p><strong>离线应用</strong>:Service Worker(Cache API + 回退策略)</p></li><li><p><strong>状态持久化</strong>:Redux/Pinia + Web Storage持久化插件</p></li></ol>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>注意事项:</h3>
<ul><li><p><strong>缓存污染</strong>:合理设置缓存键,避免数据混淆</p></li><li><p><strong>内存管理</strong>:定期清理过期缓存(特别是Cache API)</p></li><li><p><strong>安全</strong>:敏感数据避免存储在LocalStorage</p></li><li><p><strong>版本控制</strong>:静态资源使用内容哈希,避免更新问题</p></li></ul>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>缓存更新策略示例</h3>
<p>javascript</p>
<div class="jb51code"><pre class="brush:js;">// Service Worker 缓存策略:网络优先,失败用缓存
self.addEventListener('fetch', event =&gt; {
event.respondWith(
    fetch(event.request)
      .catch(() =&gt; caches.match(event.request))
);
});

// 版本化缓存:资源URL带哈希
// style.abc123.css -&gt; 强缓存一年
&lt;link href="/css/style.abc123.css" rel="external nofollow"rel="stylesheet"&gt;</pre></div>
<p>选择合适的缓存策略需要综合考虑<strong>资源类型</strong>、<strong>更新频率</strong>、<strong>数据大小</strong>和<strong>用户体验</strong>要求,通常需要组合使用多种缓存方式。</p>
<p class="maodian"><a name="_label2"></a></p><h2>Service Worker 缓存详解</h2>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>一、Service Worker 基础架构</h3>
<p class="maodian"><a name="_label3_2_4_0"></a></p><h4>1.生命周期</h4>
<blockquote><p>注册 &rarr; 安装 &rarr; 激活 &rarr; 监听事件 &rarr; 终止</p></blockquote>
<p class="maodian"><a name="_label3_2_4_1"></a></p><h4>2.核心特性</h4>
<ul><li><p><strong>独立线程</strong>:与主线程分离,不阻塞UI</p></li><li><p><strong>可编程缓存</strong>:通过Cache API精细控制</p></li><li><p><strong>请求拦截</strong>:可拦截、修改网络请求</p></li><li><p><strong>离线支持</strong>:网络不可用时仍可响应</p></li><li><p><strong>后台同步</strong>:支持后台数据同步</p></li></ul>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>二、Cache API 详解</h3>
<p class="maodian"><a name="_label3_2_5_2"></a></p><h4>1.缓存存储结构</h4>
<div class="jb51code"><pre class="brush:js;">// 打开/创建缓存
caches.open('v1').then(cache =&gt; {
// cache 对象操作
});

// 查看所有缓存
caches.keys().then(keys =&gt; {
console.log('所有缓存:', keys);
});</pre></div>
<p class="maodian"><a name="_label3_2_5_3"></a></p><h4>2.缓存操作方法</h4>
<div class="jb51code"><pre class="brush:js;">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();</pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>三、常用缓存策略</h3>
<p class="maodian"><a name="_label3_2_6_4"></a></p><h4>1.缓存优先(Cache First)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(
    caches.match(event.request)
      .then(response =&gt; {
      // 有缓存直接返回
      if (response) {
          return response;
      }
      // 否则从网络获取
      return fetch(event.request)
          .then(response =&gt; {
            // 可选:缓存新资源
            return caches.open(CACHE_NAME)
            .then(cache =&gt; {
                cache.put(event.request, response.clone());
                return response;
            });
          });
      })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_5"></a></p><h4>2.网络优先(Network First)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(
    fetch(event.request)
      .then(response =&gt; {
      // 网络成功:更新缓存
      return caches.open(CACHE_NAME)
          .then(cache =&gt; {
            cache.put(event.request, response.clone());
            return response;
          });
      })
      .catch(() =&gt; {
      // 网络失败:使用缓存
      return caches.match(event.request);
      })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_6"></a></p><h4>3.仅缓存(Cache Only)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(
    caches.match(event.request)
      .then(response =&gt; {
      if (!response) {
          // 没有缓存时返回兜底页面
          return caches.match('/offline.html');
      }
      return response;
      })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_7"></a></p><h4>4.仅网络(Network Only)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(fetch(event.request));
});</pre></div>
<p class="maodian"><a name="_label3_2_6_8"></a></p><h4>5.Stale-While-Revalidate(先旧后新)</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(
    caches.open(CACHE_NAME).then(cache =&gt; {
      return cache.match(event.request).then(cachedResponse =&gt; {
      const fetchPromise = fetch(event.request).then(networkResponse =&gt; {
          // 更新缓存
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
      });
      // 优先返回缓存,同时更新缓存
      return cachedResponse || fetchPromise;
      });
    })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_6_9"></a></p><h4>6.运行时缓存(Runtime Caching)</h4>
<div class="jb51code"><pre class="brush:js;">// 动态决定缓存策略
self.addEventListener('fetch', event =&gt; {
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);
});</pre></div>
<p class="maodian"><a name="_lab2_2_7"></a></p><h3>四、高级缓存模式</h3>
<p class="maodian"><a name="_label3_2_7_10"></a></p><h4>1.版本化缓存管理</h4>
<div class="jb51code"><pre class="brush:js;">const CACHE_VERSION = 'v2';
const CACHE_NAME = `app-cache-${CACHE_VERSION}`;
const OLD_CACHES = ['app-cache-v1'];

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

// 激活时:清理旧缓存
self.addEventListener('activate', event =&gt; {
event.waitUntil(
    caches.keys().then(cacheNames =&gt; {
      return Promise.all(
      cacheNames.map(cacheName =&gt; {
          if (OLD_CACHES.includes(cacheName)) {
            return caches.delete(cacheName);
          }
      })
      );
    }).then(() =&gt; self.clients.claim()) // 控制所有客户端
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_11"></a></p><h4>2.缓存过期策略</h4>
<div class="jb51code"><pre class="brush:js;">// 定期清理过期缓存
self.addEventListener('activate', event =&gt; {
event.waitUntil(
    caches.open(CACHE_NAME).then(cache =&gt; {
      return cache.keys().then(requests =&gt; {
      return Promise.all(
          requests.map(request =&gt; {
            return cache.match(request).then(response =&gt; {
            if (!response) return null;
            
            // 检查缓存时间
            const cachedDate = new Date(response.headers.get('date'));
            const now = new Date();
            const cacheAge = now - cachedDate;
            
            // 超过7天删除
            if (cacheAge &gt; 7 * 24 * 60 * 60 * 1000) {
                return cache.delete(request);
            }
            return null;
            });
          })
      );
      });
    })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_12"></a></p><h4>3.请求预缓存</h4>
<div class="jb51code"><pre class="brush:js;">// 预缓存可能访问的资源
self.addEventListener('install', event =&gt; {
event.waitUntil(
    caches.open(CACHE_NAME).then(cache =&gt; {
      // 缓存当前页面资源
      return cache.addAll(currentPageResources);
    }).then(() =&gt; {
      // 预加载其他页面资源(不阻塞安装)
      self.skipWaiting();
      return preloadFutureResources();
    })
);
});

function preloadFutureResources() {
return fetch('/api/predict-resources')
    .then(res =&gt; res.json())
    .then(resources =&gt; {
      return caches.open(CACHE_NAME)
      .then(cache =&gt; cache.addAll(resources));
    })
    .catch(err =&gt; console.log('预加载失败:', err));
}</pre></div>
<p class="maodian"><a name="_label3_2_7_13"></a></p><h4>4.响应定制与降级</h4>
<div class="jb51code"><pre class="brush:js;">self.addEventListener('fetch', event =&gt; {
event.respondWith(
    caches.match(event.request)
      .then(response =&gt; {
      if (response) return response;
      
      return fetch(event.request)
          .then(networkResponse =&gt; {
            // 只缓存成功的响应
            if (networkResponse.ok) {
            const clonedResponse = networkResponse.clone();
            caches.open(CACHE_NAME)
                .then(cache =&gt; cache.put(event.request, clonedResponse));
            }
            return networkResponse;
          })
          .catch(error =&gt; {
            // 根据请求类型返回不同降级内容
            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' }
            });
          });
      })
);
});</pre></div>
<p class="maodian"><a name="_label3_2_7_14"></a></p><h4>5.智能缓存策略</h4>
<div class="jb51code"><pre class="brush:js;">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;
   
    // 不缓存大文件(&gt;10MB)
    const contentLength = response.headers.get('content-length');
    if (contentLength &gt; 10 * 1024 * 1024) return false;
   
    return true;
}
}</pre></div>
<p class="maodian"><a name="_lab2_2_8"></a></p><h3>五、性能优化技巧</h3>
<p class="maodian"><a name="_label3_2_8_15"></a></p><h4>1.缓存分层策略</h4>
<div class="jb51code"><pre class="brush:js;">// 多级缓存:内存 + 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);
}
}</pre></div>
<p class="maodian"><a name="_label3_2_8_16"></a></p><h4>2.缓存预热</h4>
<div class="jb51code"><pre class="brush:js;">// 空闲时预加载资源
self.addEventListener('message', event =&gt; {
if (event.data.type === 'PRELOAD_RESOURCES') {
    event.waitUntil(
      preloadResources(event.data.urls)
    );
}
});

function preloadResources(urls) {
return Promise.all(
    urls.map(url =&gt; {
      return fetch(url)
      .then(response =&gt; {
          if (response.ok) {
            return caches.open(CACHE_NAME)
            .then(cache =&gt; cache.put(url, response));
          }
      })
      .catch(() =&gt; {/* 静默失败 */});
    })
);
}</pre></div>
<p class="maodian"><a name="_lab2_2_9"></a></p><h3>六、调试与监控</h3>
<p class="maodian"><a name="_label3_2_9_17"></a></p><h4>1.调试工具</h4>
<div class="jb51code"><pre class="brush:js;">// Service Worker调试日志
self.addEventListener('fetch', event =&gt; {
console.log(` Fetch: ${event.request.url}`);
console.log(` Mode: ${event.request.mode}`);
console.log(` Cache: ${caches.keys()}`);
});</pre></div>
<p class="maodian"><a name="_label3_2_9_18"></a></p><h4>2.缓存状态监控</h4>
<div class="jb51code"><pre class="brush:js;">// 监控缓存使用情况
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 =&gt; {
      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) =&gt; sum + item.size, 0);

return {
    totalItems: stats.length,
    totalSize,
    items: stats
};
}</pre></div>
<p class="maodian"><a name="_lab2_2_10"></a></p><h3>七、最佳实践</h3>
<p class="maodian"><a name="_label3_2_10_19"></a></p><h4>推荐做法</h4>
<ol><li><p><span><strong>资源分类</strong>:不同资源类型使用不同策略</span></p></li><li><p><span><strong>版本控制</strong>:每次更新使用新缓存名称</span></p></li><li><p><span><strong>渐进增强</strong>:缓存作为性能优化,不是必需功能</span></p></li><li><p><span><strong>监控告警</strong>:监控缓存命中率和错误率</span></p></li><li><p><span><strong>容量控制</strong>:定期清理过期缓存</span></p></li></ol>
<p class="maodian"><a name="_label3_2_10_20"></a></p><h4>注意事项</h4>
<ol><li><p><strong>HTTPS要求</strong>:生产环境必须使用HTTPS</p></li><li><p><span><strong>缓存污染</strong>:避免缓存个性化内容</span></p></li><li><p><strong>内存泄漏</strong>:定期清理无用缓存</p></li><li><p><strong>更新策略</strong>:明确缓存失效机制</p></li><li><p><strong>回退方案</strong>:网络和缓存都失败时的处理</p></li></ol>
<p class="maodian"><a name="_lab2_2_11"></a></p><h3>策略选择指南</h3>
<table><thead><tr><th><span>场景</span></th><th>推荐策略</th><th>理由</th></tr></thead><tbody><tr><td><span>静态资源(版本化)</span></td><td>缓存优先 + 长期缓存</td><td>内容不变,性能最优</td></tr><tr><td><span>API数据(实时性高)</span></td><td>网络优先</td><td>保证数据最新</td></tr><tr><td><span>用户生成内容</span></td><td>Stale-While-Revalidate</td><td>平衡速度与新鲜度</td></tr><tr><td><span>离线应用</span></td><td>缓存优先 + 降级页面</td><td>保证基本功能可用</td></tr><tr><td><span>大文件(视频/图片)</span></td><td>按需缓存 + 容量控制</td><td>避免存储空间占用过大</td></tr></tbody></table>
<p>Service Worker缓存是现代Web应用性能优化的核心工具,合理使用可以显著提升用户体验,特别是在弱网和离线场景下。</p>
<p class="maodian"><a name="_label3"></a></p><h2>总结</h2>
頁: [1]
查看完整版本: 前端缓存方式对比表和Service Worker缓存详细讲解