企业H5站点升级PWA (二)

下面直接给一套在你现有结构上最小侵入地接入 Service Worker 的方案,做到:

  • 保留你现在的导航和动态 fetch 子页逻辑;
  • 只缓存静态资源(HTML/JS/CSS/图片),不拦截你的业务 API;
  • 支持基本的 PWA:可安装、离线时至少能打开壳页面。

分三步:


1. 新增 sw.js(Service Worker)

放在和 index.html 同级的 wwwroot 目录:

javascript 复制代码
const CACHE_NAME = 'app-static-v1';

// 这里列出首要静态资源(至少把当前壳和主 JS/CSS 缓存起来)
const PRECACHE_URLS = [
    './',
    './index.html',
    './index.css',
    './index.js',
    './homeConfig.js',
    './pda.js',
    './auth.js',
    './bootstrap.bundle.min.js', 
    './manifest.json'
];

// 安装阶段:预缓存核心静态资源
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CACHE_NAME).then((cache) => {
            return cache.addAll(PRECACHE_URLS);
        })
    );
    // 跳过等待,立即进入 activate
    self.skipWaiting();
});

// 激活阶段:清理旧版本缓存
self.addEventListener('activate', (event) => {
    event.waitUntil(
        caches.keys().then((keys) => {
            return Promise.all(
                keys.map((key) => {
                    if (key !== CACHE_NAME) {
                        return caches.delete(key);
                    }
                    return null;
                })
            );
        })
    );
    self.clients.claim();
});

// 请求拦截:对静态资源使用 cache-first,对 API / 动态接口放行
self.addEventListener('fetch', (event) => {
    const { request } = event;

    // 只处理 GET
    if (request.method !== 'GET') {
        return;
    }

    const url = new URL(request.url);

    // 1. 避免干扰 API(根据你的 API_BASE 和常见前缀排除)
    //    如果你 API 跑在同域 /api 下,可以按需修改
    if (url.pathname.startsWith('/api/')
        || url.pathname.startsWith('/swagger')
        || url.pathname.includes('/signalr')) {
        return;
    }

    // 2. 对 HTML/CSS/JS/图片等静态资源做 cache-first
    //    这里用简单的策略:先查缓存,没命中再走网络并回填
    event.respondWith(
        caches.match(request).then((cached) => {
            if (cached) {
                return cached;
            }

            return fetch(request)
                .then((response) => {
                    // 只缓存 200 的基础静态资源
                    if (!response || response.status !== 200 || response.type === 'opaque') {
                        return response;
                    }

                    const clone = response.clone();
                    caches.open(CACHE_NAME).then((cache) => {
                        cache.put(request, clone);
                    });

                    return response;
                })
                .catch(() => {
                    // 离线兜底:请求 HTML 时,回退到首页
                    if (request.headers.get('accept')?.includes('text/html')) {
                        return caches.match('./index.html');
                    }
                    return new Response('网络不可用,且本资源未被缓存。', {
                        status: 503,
                        headers: { 'Content-Type': 'text/plain; charset=utf-8' }
                    });
                });
        })
    );
});

要点:

  • 只对静态资源起作用,不会挡住你 fetch(url) 去拉 pda.html / add.html 等页面(这些也是静态文件,能自动享受缓存)。
  • 避免拦 API:简单按路径前缀排除,你可以根据实际 API 路径调整。

2. 在 index.html 注册 Service Worker

在你已有的 index.html 里(</body> 前)加一段注册逻辑。你当前文件尾部大致是:

html 复制代码
    <div class="footer hide" id="footer">
        <button type="button" data-url="home" onclick="navigateTo(this)">首页</button>
        <button type="button" onclick="alert('我的')">我的</button>
    </div>
    <script src="bootstrap.bundle.min.js"></script>
    <script src="index.js"></script>
</body>
</html>

改为(新增一段 serviceWorker 注册脚本):

html 复制代码
    <div class="footer hide" id="footer">
        <button type="button" data-url="home" onclick="navigateTo(this)">首页</button>
        <button type="button" onclick="alert('我的')">我的</button>
    </div>
    <script src="bootstrap.bundle.min.js"></script>
    <script src="index.js"></script>
    <script>
        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.error('Service Worker 注册失败:', error);
                    });
            });
        }
    </script>
</body>
</html>

不动你现有逻辑,只是在页面加载后注册 sw.js


3. 确认 manifest.json 和 HTTPS

index.html 已经有:

html 复制代码
<link rel="manifest" href="manifest.json">

检查两点即可:

  1. manifest.json 至少要包含:
json 复制代码
{
  "name": "App",
  "short_name": "MyApp",
  "start_url": "./",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "densen26.jpg",
      "sizes": "192x192",
      "type": "image/jpeg"
    }
  ]
}
  1. PWA 要求:
    • 浏览器访问时必须是 HTTPS 或 http://localhost
    • 如果你是 WinForms + WebView2 本地文件加载,Service Worker 默认是不工作的,需要:
      • 改为在本地起一个 http://127.0.0.1:xxxx 小服务器托管 wwwroot
      • WebView2 加载这个本地 http 地址。

4. 和你现有 index.js 的配合关系

这套改造对你当前代码完全透明:

  • loadPagefetch(url)
    • 第一次仍然从网络拉 pda.html / add.html
    • 被 Service Worker 和浏览器缓存后,再次访问会命中缓存,体验更快。
  • 动态加载的 homeConfig.js / pda.js 等脚本,只要它们的 URL 在缓存策略覆盖范围内,也会被缓存。
  • 你自己的 reload() / initpda() / renderHomeButtons() 完全不需要改。

如果后面你想升级策略,比如:

  • 首次打开时就预缓存 pda.htmladd.html 等子页面;
  • 对某些资源用 stale-while-revalidate 策略;

只需要再调整 PRECACHE_URLSfetch 里的逻辑即可。


相关推荐
Penge6667 小时前
Go 接口编译期断言
后端
我是一颗柠檬7 小时前
【MySQL全面教学】MySQL面试高频考点汇总Day15(2026年)
数据库·后端·mysql·面试
橙淮7 小时前
并发编程(六)
java·jvm
拽着尾巴的鱼儿7 小时前
springboot openfeign 自定义feign 接口重试机制
java·spring boot·后端
白露与泡影8 小时前
2026大厂Java面试题大全!牛客网最新版
java·开发语言
Ceelog8 小时前
久坐党自救指南:屏幕前 8 小时,身体到底在经历什么
前端·后端
EntyIU8 小时前
JVM内存与GC笔记
java·jvm·笔记
XS0301069 小时前
并发编程 六
java·后端
yaoxin5211239 小时前
419. 现代 Java IO 最佳实践 - 写入文本文件
java·windows·python
雪宫街道9 小时前
synchronized 锁的范围:对象锁、类锁与代码块锁
java·jvm·后端·面试