导读:本期聚焦于小伙伴创作的《如何构建一个渐进式Web应用(PWA)的JavaScript核心逻辑?》,敬请观看详情。浏览器原生不支持离线缓存时,页面一旦断网就直接白屏,这是Web应用最容易被用户放弃的痛点。渐进式Web应用通过Service Worker拦截请求与本地缓存,配合manifest配置实现可安装体验。核心逻辑并不复杂:先注册生命周期脚本,再在install阶段预缓存静态资源,activate阶段清理旧缓存,fetch阶段按缓存优先或网络优先策略响应。掌握这几步,就能让普通站点具备离线打开、消息推送与桌面入口能力,无需依赖框架也能落地。

渐进式Web应用(PWA)之所以能像原生应用一样离线可用、可被安装到主屏,关键在于两套JavaScript机制:一是Service Worker提供的后台网络代理与缓存控制,二是Web App Manifest驱动的展示与安装行为。本文从零梳理在不借助任何构建框架的情况下,如何用原生JavaScript写出PWA的核心逻辑。

如何构建一个渐进式Web应用(PWA)的JavaScript核心逻辑?

一、Web App Manifest的配置与关联

Manifest是一个JSON文件,用来告诉浏览器该Web应用的名称、图标、启动样式等信息。虽然它不属于JavaScript执行逻辑,但必须由页面通过link标签引入,否则系统不会弹出安装提示。我们通常在HTML头部写入如下关系:

注意这里提到的<link>标签是文档元信息声明,不是脚本,也不是函数调用。它的rel属性值固定为manifest,href指向json文件路径。当浏览器检测到合规manifest且已注册Service Worker,才会在地址栏显示安装图标。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <link rel="manifest" href="/manifest.json">
  <title>简易PWA示例</title>
</head>
<body>
  <script src="/sw-register.js"></script>
</body>
</html>

对应的manifest内容需要包含name、short_name、icons与start_url等字段。icons至少要提供一张192像素和一张512像素的PNG,否则部分安卓设备会忽略安装请求。start_url建议写成相对路径,避免不同部署子目录导致跳转异常。

{
  "name": "我的离线笔记",
  "short_name": "笔记",
  "start_url": "./index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3367d6",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

二、Service Worker的注册逻辑

Service Worker本质是一个独立于页面主线程的脚本,它能拦截当前域下的网络请求。注册动作必须在页面脚本里通过navigator.serviceWorker完成,且只能在HTTPS或localhost环境执行。下面的sw-register.js展示了最基础的注册方式。

代码中我们首先判断navigator对象上是否存在serviceWorker属性,避免在不支持的老浏览器中报错。注册成功后,控制台会显示Worker作用域。需要强调的是,register()接收的路径相对于域名根,而非调用脚本所在目录,写错路径会导致作用域受限。

// sw-register.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function () {
    navigator.serviceWorker.register('/sw.js')
      .then(function (registration) {
        console.log('Service Worker注册成功,作用域为:', registration.scope);
      })
      .catch(function (error) {
        console.log('Service Worker注册失败:', error);
      });
  });
}

注册本身不缓存任何资源,真正的缓存策略写在sw.js里。分离注册与逻辑文件,有利于后期只更新缓存脚本而不动页面引用。若希望更新立即生效,可在注册时追加updatefound事件监听,但生产环境通常依赖浏览器静默更新机制。

三、Service Worker生命周期与缓存管理

sw.js内部通过监听install、activate和fetch三个事件实现核心控制。install阶段适合预缓存核心静态文件,activate阶段负责删除过期缓存,fetch阶段决定如何响应请求。以下示例采用缓存优先并回退网络的策略。

在install中使用event.waitUntil()保证缓存操作完成前Worker不进入下一阶段。cache.addAll()接受的URL数组必须全部请求成功,只要一个404就会让安装失败,因此建议只放必定存在的骨架文件。activate中遍历所有缓存名,剔除非当前版本号缓存,防止存储空间无限增长。

// sw.js
const CACHE_NAME = 'pwa-cache-v1';
const PRECACHE_URLS = [
  './',
  './index.html',
  './styles.css',
  './app.js',
  './icon-192.png'
];

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function (cache) {
        return cache.addAll(PRECACHE_URLS);
      })
  );
});

self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (keys) {
      return Promise.all(
        keys.filter(function (key) {
          return key !== CACHE_NAME;
        }).map(function (key) {
          return caches.delete(key);
        })
      );
    })
  );
});

这种版本号命名方式(pwa-cache-v1)让每次发布只需改常量即可触发旧缓存清理。如果项目迭代频繁,可以把版本写入构建变量,由服务端模板注入,避免手动维护失误。activate之后旧Worker才会停止,因此用户第二次访问才能拿到新缓存。

四、fetch事件的请求拦截策略

fetch监听是PWA离线能力的核心。每当页面发起请求,Worker都会先收到事件。下面代码演示缓存优先逻辑:先查缓存,命中则返回,未命中再走网络并把响应克隆存入缓存,保证下次离线可用。

注意response.clone()的使用,因为响应流只能读取一次,直接返回的同时无法再写缓存。对于HTML导航请求,也可改为网络优先、失败回退缓存的策略以提升实时性。若请求是POST或非同源,通常直接放行,不参与缓存,避免数据错乱。

self.addEventListener('fetch', function (event) {
  if (event.request.method !== 'GET') {
    return;
  }
  event.respondWith(
    caches.match(event.request).then(function (cached) {
      if (cached) {
        return cached;
      }
      return fetch(event.request).then(function (response) {
        if (!response || response.status !== 200 || response.type !== 'basic') {
          return response;
        }
        var responseToCache = response.clone();
        caches.open(CACHE_NAME).then(function (cache) {
          cache.put(event.request, responseToCache);
        });
        return response;
      }).catch(function () {
        // 离线且缓存未命中时返回兜底页面
        return caches.match('./index.html');
      });
    })
  );
});

实际项目中,可针对API请求与静态资源使用不同缓存桶。例如图片走缓存优先,接口走网络优先并短期缓存。通过解析event.request.url或destination属性做分支,能显著改善弱网体验,也不会让过期数据长期滞留。

五、调试与更新注意事项

开发时打开Chrome开发者工具的Application面板,可查看Service Worker状态、缓存存储及Manifest解析结果。若修改了sw.js,需手动点击SkipWaiting或重新加载触发更新。由于缓存优先,页面逻辑更新后若未升版本号,用户可能长期看不到变化。

另一个常见误区是在fetch里缓存了跨域字体或脚本却未校验response.type,导致opaque响应无法读取状态而缓存失败。建议对跨域资源单独命名缓存,并在服务器端配置正确的CORS头。只要遵循生命周期与缓存隔离原则,原生JavaScript就能支撑起稳定的PWA核心逻辑。

PWAservice_workermanifest_json修改时间:2026-08-05 18:06:35

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