侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1824 篇文章
  • 累计收到 0 条评论

PWA 中如何设计 Service Worker 缓存策略实现离线优先体验?

2025-12-11 / 0 评论 / 3 阅读

题目

PWA 中如何设计 Service Worker 缓存策略实现离线优先体验?

信息

  • 类型:问答
  • 难度:⭐⭐

考点

Service Worker 缓存策略,离线体验优化,缓存更新机制

快速回答

实现离线优先体验的核心要点:

  • 缓存策略选择:根据资源类型使用 CacheFirst(静态资源)和 NetworkFirst(动态内容)策略
  • 预缓存关键资源:在 Service Worker install 阶段缓存核心静态文件
  • 动态缓存机制:在 fetch 事件中按策略处理请求
  • 缓存版本控制:通过更改缓存名称强制更新缓存
  • 离线回退方案:提供自定义离线页面和备用内容
## 解析

一、核心原理说明

Service Worker 作为独立线程可拦截网络请求,配合 Cache API 实现:

  • 离线优先:优先从缓存返回响应,无缓存时回退网络
  • 缓存策略:根据资源类型选择不同策略(如图)
    缓存策略流程图
  • 更新机制:通过修改缓存名称触发更新,清理旧缓存

二、代码实现示例

1. 注册 Service Worker

// main.js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW registered'))
}

2. 预缓存关键资源 (sw.js)

const CACHE_NAME = 'v1-core-assets';
const PRE_CACHE = [
  '/',
  '/styles/main.css',
  '/scripts/app.js',
  '/offline.html'
];

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

3. 缓存策略实现 (sw.js)

// 网络优先(适合频繁更新的内容)
const networkFirst = async (request) => {
  try {
    const networkResponse = await fetch(request);
    const cache = await caches.open('dynamic-cache');
    cache.put(request, networkResponse.clone());
    return networkResponse;
  } catch {
    return caches.match(request) || caches.match('/offline.html');
  }
};

// 缓存优先(适合静态资源)
const cacheFirst = async (request) => {
  const cachedResponse = await caches.match(request);
  if (cachedResponse) return cachedResponse;

  try {
    const networkResponse = await fetch(request);
    const cache = await caches.open('static-cache');
    cache.put(request, networkResponse.clone());
    return networkResponse;
  } catch {
    return caches.match('/offline.html');
  }
};

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);

  if (url.pathname.includes('/api/')) {
    event.respondWith(networkFirst(event.request));
  } else {
    event.respondWith(cacheFirst(event.request));
  }
});

4. 缓存清理与更新

self.addEventListener('activate', event => {
  const cacheWhitelist = ['v2-core-assets'];

  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (!cacheWhitelist.includes(cacheName)) {
            return caches.delete(cacheName); // 删除旧缓存
          }
        })
      );
    })
  );
});

三、最佳实践

  • 策略选择
    • CSS/JS/图片:Cache First + 内容哈希
    • API 数据:Network First + 短时间缓存
    • HTML 文件:Network First(确保内容更新)
  • 缓存限制:设置缓存数量/大小上限,避免存储膨胀
  • 更新提示:检测 Service Worker 更新后提示用户刷新
  • 降级方案:使用 navigator.onLine 检测网络状态

四、常见错误

  • 缓存策略错配:对动态内容使用 Cache First 导致数据过期
  • 缓存爆炸:未设置缓存上限导致存储空间耗尽
  • 版本控制缺失:未更新缓存名称导致用户获取旧资源
  • 作用域错误:Service Worker 文件未放在根目录导致无法拦截全部请求
  • 忽略 HTTPS:生产环境未使用 HTTPS 导致 Service Worker 无法注册

五、扩展知识

  • Workbox 工具库:简化缓存策略配置
    import { registerRoute } from 'workbox-routing';
    import { CacheFirst, NetworkFirst } from 'workbox-strategies';
    
    registerRoute(
      ({request}) => request.destination === 'image',
      new CacheFirst()
    );
  • 后台同步:使用 SyncManager 实现离线操作队列
  • 推送通知Push API + Notification API 增强用户粘性
  • 性能优化:结合 Lighthouse 审计 PWA 核心指标(速度指数、可交互时间)