如何用Service Worker实现离线可用的PWA应用?

来源:APP编程网作者:本地能跑头衔:程序员
导读:本期聚焦于小伙伴创作的《如何用Service Worker实现离线可用的PWA应用?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用Service Worker实现离线可用的PWA应用?》有用,将其分享出去将是对创作者最好的鼓励。

Service Worker是运行在浏览器后台的脚本,独立于主线程,能够拦截和处理网络请求,是实现PWA离线可用能力的核心。它可以在用户首次访问页面时缓存静态资源和接口数据,后续无网络时直接从缓存中读取内容,保证应用正常使用。

如何用Service Worker实现离线可用的PWA应用?

Service Worker的基础概念

Service Worker本质上是一个事件驱动的worker,它不能直接操作DOM,也不能访问window对象,但是可以通过postMessage方法和主线程通信。它的生命周期包括注册、安装、激活、拦截请求等阶段,每个阶段都有对应的事件可以监听处理。

核心生命周期阶段

  • 注册阶段:主线程通过navigator.serviceWorker.register方法注册Service Worker脚本,浏览器会在后台下载并执行脚本。
  • 安装阶段:注册成功后触发install事件,这个阶段适合预缓存应用的核心静态资源,比如HTML、CSS、JS、图片等。
  • 激活阶段:安装完成后触发activate事件,这个阶段可以清理旧的缓存,保证缓存内容的版本一致性。
  • 拦截请求阶段:激活后的Service Worker会拦截页面发出的网络请求,开发者可以在fetch事件中自定义请求处理逻辑,比如优先使用缓存还是优先请求网络。

实现离线PWA的完整步骤

第一步:注册Service Worker

首先需要在主页面的入口JS中注册Service Worker,注意Service Worker脚本必须运行在HTTPS环境或者localhost本地环境,否则浏览器会拒绝注册。

// 检查浏览器是否支持Service Worker
if ('serviceWorker' in navigator) {
  // 页面加载完成后注册
  window.addEventListener('load', () => {
    // 注册sw.js作为Service Worker脚本,作用域为当前目录
    navigator.serviceWorker.register('/sw.js')
      .then((registration) => {
        console.log('Service Worker注册成功,作用域为:', registration.scope);
      })
      .catch((error) => {
        console.log('Service Worker注册失败:', error);
      });
  });
}

第二步:编写Service Worker脚本实现缓存

创建sw.js文件,在里面编写安装、激活、请求拦截的逻辑,实现资源的缓存和读取。

// 定义缓存名称和需要预缓存的资源列表
const CACHE_NAME = 'pwa-cache-v1';
const PRE_CACHE_URLS = [
  '/',
  '/index.html',
  '/style.css',
  '/main.js',
  '/logo.png'
];

// 监听安装事件,预缓存核心资源
self.addEventListener('install', (event) => {
  // 等待缓存操作完成后再结束安装阶段
  event.waitUntil(
    // 打开指定名称的缓存
    caches.open(CACHE_NAME)
      .then((cache) => {
        console.log('开始预缓存资源');
        // 将预定义的资源列表添加到缓存中
        return cache.addAll(PRE_CACHE_URLS);
      })
      .then(() => {
        console.log('预缓存完成');
        // 强制跳过等待,直接进入激活阶段
        return 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(() => {
        // 让Service Worker立即控制所有当前打开的页面
        return self.clients.claim();
      })
  );
});

// 监听fetch事件,拦截网络请求,实现缓存优先策略
self.addEventListener('fetch', (event) => {
  // 只处理GET请求,非GET请求直接走网络
  if (event.request.method !== 'GET') {
    return;
  }
  // 用缓存优先的策略处理请求
  event.respondWith(
    // 先查找缓存中是否有对应的请求结果
    caches.match(event.request)
      .then((cachedResponse) => {
        // 如果缓存中有结果,直接返回缓存内容
        if (cachedResponse) {
          console.log('从缓存中读取:', event.request.url);
          return cachedResponse;
        }
        // 缓存中没有结果,发起网络请求
        console.log('发起网络请求:', event.request.url);
        return fetch(event.request)
          .then((networkResponse) => {
            // 网络请求成功后,将结果克隆一份存入缓存,再返回给页面
            // 克隆是因为response只能被读取一次
            const responseClone = networkResponse.clone();
            caches.open(CACHE_NAME)
              .then((cache) => {
                cache.put(event.request, responseClone);
              });
            return networkResponse;
          })
          .catch(() => {
            // 网络请求失败,且缓存中没有内容,返回默认的错误页面(可选)
            // 这里可以根据需求返回离线提示页面
            return new Response('当前无网络,且资源未缓存,无法访问', {
              status: 503,
              statusText: 'Service Unavailable'
            });
          });
      })
  );
});

第三步:添加PWA必需的清单文件

要实现完整的PWA能力,还需要添加manifest.json文件,声明应用的名称、图标、启动方式等信息,让浏览器识别这是一个PWA应用。

{
  "name": "离线PWA示例",
  "short_name": "PWA示例",
  "description": "使用Service Worker实现的离线可用PWA应用",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

然后在HTML页面的<head>标签中添加清单文件的引用:

<link rel="manifest" href="/manifest.json">

缓存策略的选择

上面的示例使用了缓存优先的策略,实际开发中可以根据资源类型选择不同的缓存策略:

策略名称适用场景实现逻辑
缓存优先静态资源、不常变化的接口先查缓存,缓存有则返回,无则请求网络并缓存
网络优先实时性要求高的接口先请求网络,成功则返回并缓存,失败则查缓存
仅缓存完全离线的资源只从缓存读取,不接受网络请求
仅网络不需要离线的资源只走网络请求,不缓存

注意事项

  • Service Worker的更新需要修改脚本内容,浏览器检测到脚本变化后会重新安装新版本,新版本激活后会控制新的页面,旧的页面仍然由旧版本的Service Worker控制,直到页面重新加载。
  • 缓存的资源需要定期更新,避免用户一直使用旧版本的内容,可以在激活阶段或者fetch事件中做版本校验。
  • 不要缓存太大的资源,避免占用过多的用户存储空间,浏览器对单个域名的缓存大小有限制。
  • 本地开发时使用localhost可以正常调试Service Worker,部署到线上必须使用HTTPS协议,否则Service Worker无法注册生效。
Service Worker的功能不止于离线缓存,还可以实现消息推送、后台同步等能力,结合PWA的其他特性,可以让网页应用拥有接近原生应用的使用体验。

Service_WorkerPWA离线缓存前端开发修改时间:2026-06-15 04:27:42

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。