You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PWA缓存策略为何严重影响性能?LCP飙升4秒求排查

问题:PWA博客首页离线缓存导致Lighthouse性能暴跌

我正在开发首个PWA博客应用,目标是让首页支持离线访问(首页无MongoDB查询,但包含大量图片与CSS)。采用的缓存逻辑为:拦截请求,优先从缓存读取资源,无缓存则发起网络请求。技术栈是Node.js服务端渲染+MongoDB+EJS模板。但实现后,Lighthouse性能评分从约94降至76,LCP指标大幅上升4秒,和预期的性能提升完全相反。推测问题出在Fetch事件的缓存处理逻辑中,但不知道怎么优化,寻求帮助。


相关代码

服务端首页路由

app.get("/", resetVerifiedVariable, (req, res) => {
  res.render("home.ejs");
});

页面头部Service Worker注册脚本片段

<head>
  ...
<!-- REGISTER SERVICE WORKER -->
  <script src="/scripts/initiateServiceWorker.js"></script>
<!-- REGISTER SERVICE WORKER -->
  ...
</head>

Service Worker注册代码

// ###################################
//    REGISTER SERVICE WORKER - PWA
// ###################################
// Check if the browser supports the serviceWorker

if ("serviceWorker" in navigator) {
  navigator.serviceWorker
    .register("../serviceWorker.js")
    .then((registrationObject) =>
      console.log("ServiceWorker registered", registrationObject)
    )
    .catch((error) => console.log("ServiceWorker NOT registered", error));
}

缓存实现代码

const staticCacheName = "staticServiceWorker-v1";

const staticAssets = ["/", "/img/upchart-mobile.webp", "/scripts/index.js", "css/main.css"];


// ############################
// INSTALL
// ############################
self.addEventListener("install", (event) => {
  const preCache = async () => {
    const cache = await caches.open(staticCacheName);
    return cache.addAll(staticAssets);
  };
  event.waitUntil(preCache());
});

// ############################
// ACTIVATE
// ############################
self.addEventListener("activate", (event) => {
  //console.log("ServiceWorker activated");
});

// ############################
// FETCH
// ############################

self.addEventListener("fetch", (event) => {
  console.log(event);

  const checkCacheMatch = async () => {
    const result = await caches.match(event.request);

    return result || fetch(event.request);
  };
  event.respondWith(checkCacheMatch());
});

问题分析与优化方案

1. 移除不必要的控制台打印

fetch事件里的console.log(event)会在每个请求时打印庞大的事件对象,产生大量性能开销,直接删除这行代码。

2. 修正缓存策略逻辑

当前的Cache-First策略存在两个问题:一是会拦截所有请求(包括POST等不可缓存的请求),二是首次缓存未命中时没有后台更新缓存的逻辑,导致后续访问无法利用缓存。修改后的Fetch事件逻辑:

self.addEventListener("fetch", (event) => {
  // 只处理GET请求,跳过其他类型请求避免异常
  if (event.request.method !== 'GET') return;

  const handleRequest = async () => {
    const cache = await caches.open(staticCacheName);
    const cachedResponse = await cache.match(event.request);
    
    // 有缓存直接返回,同时后台发起网络请求更新缓存(不阻塞当前响应)
    if (cachedResponse) {
      fetch(event.request).then(response => {
        cache.put(event.request, response.clone());
      });
      return cachedResponse;
    }

    // 无缓存则走网络,成功后存入缓存供下次使用
    const networkResponse = await fetch(event.request);
    cache.put(event.request, networkResponse.clone());
    return networkResponse;
  };

  event.respondWith(handleRequest());
});

3. 优化预缓存资源

  • 预缓存只保留核心资源:页面本身、主CSS、主JS,大量图片放到首次加载后通过Fetch事件缓存,避免预缓存过大延长Service Worker安装时间。
  • 检查资源路径:css/main.css改为绝对路径/css/main.css,避免路径错误导致缓存失败。

4. 调整Service Worker注册时机

头部注册会阻塞页面渲染,将注册逻辑放到DOMContentLoaded事件触发,或者移到页面底部:

// 修改initiateServiceWorker.js
if ("serviceWorker" in navigator) {
  window.addEventListener('DOMContentLoaded', () => {
    navigator.serviceWorker
      .register("../serviceWorker.js")
      .then((registrationObject) =>
        console.log("ServiceWorker registered", registrationObject)
      )
      .catch((error) => console.log("ServiceWorker NOT registered", error));
  });
}

5. 清理旧缓存并激活控制

当前Activate事件未处理旧缓存,版本更新后旧缓存会占用空间且可能导致资源不更新,修改Activate事件:

self.addEventListener("activate", (event) => {
  const clearOldCaches = async () => {
    const cacheNames = await caches.keys();
    await Promise.all(
      cacheNames.filter(name => name !== staticCacheName)
        .map(name => caches.delete(name))
    );
    // 立即接管所有打开的页面,确保新缓存生效
    await self.clients.claim();
  };
  event.waitUntil(clearOldCaches());
});

6. LCP专项优化

LCP上升说明核心图片加载变慢:

  • 确保图片本身已优化:压缩体积、使用WebP格式、添加响应式属性。
  • 验证LCP图片是否被正确缓存:可以在Chrome DevTools的Application面板查看缓存内容,确认图片是否存入缓存。

内容的提问来源于stack exchange,提问作者Anthony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:01:17