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

如何实现Service Worker后台API拉取与离线通知功能

Service Worker后台操作与离线通知实现指南

核心原理

  • Service Worker是独立于网页的后台脚本,只要浏览器处于运行状态,即使标签页关闭,它也能被推送事件唤醒或通过后台同步API执行任务(受浏览器休眠策略限制,但核心功能不受标签页状态影响)
  • 用Push API接收服务器主动推送的消息,触发桌面通知;用Background Sync API实现标签页关闭后的后台数据拉取
  • 数据缓存可通过Cache API灵活控制,按需选择是否缓存API返回内容

实现步骤

1. 基础:注册Service Worker与推送订阅

在网页主脚本中完成Service Worker注册,申请通知权限并订阅推送服务,同时将订阅信息同步到服务器以便后续推送。

2. 后台数据拉取(可选缓存)

在Service Worker中监听sync事件,触发后台API请求,可结合Cache API缓存请求结果,拉取完成后主动触发通知。

3. 推送通知接收

Service Worker监听push事件,收到服务器推送后立即展示通知,确保用户在标签页关闭时也能收到提醒。

4. 标签页关闭触发逻辑

通过网页的beforeunload事件触发后台同步注册,或由服务器在检测到用户离线后主动发起推送。

修正后完整代码

service-worker.js

// 监听推送事件,收到服务器消息后展示通知
self.addEventListener("push", (e) => {
    const data = e.data.json();
    console.log("收到推送消息...");
    // 用waitUntil保证通知展示完成前Service Worker不被销毁
    e.waitUntil(
        self.registration.showNotification(data.title, {
            body: data.description,
            icon: data.icon,
        })
    );
});

// 监听后台同步事件,执行API数据拉取(标签页关闭后仍能触发)
self.addEventListener("sync", (e) => {
    if (e.tag === "fetch-data") {
        e.waitUntil(
            fetch("/api/data-endpoint")
                .then(response => {
                    // 可选:缓存请求结果
                    if (response.ok) {
                        return caches.open("data-cache-v1").then(cache => {
                            return cache.put("/api/data-endpoint", response.clone());
                        });
                    }
                    throw new Error("API请求失败");
                })
                .then(() => {
                    // 拉取成功后主动通知用户
                    return self.registration.showNotification("数据更新", {
                        body: "后台已完成最新数据拉取",
                        icon: "/icons/notification-icon.png"
                    });
                })
                .catch(err => console.error("后台拉取失败:", err))
        );
    }
});

index.js

// VAPID公钥,用于推送服务身份验证
const publicVapidKey = "BJthRQ5myDgc7OSXzPCMftGw-n16F7zQBEN7EUD6XxcfTTvrLGWSIG7y_JxiWtVlCFua0S8MTB5rPziBqNx1qIo";

// 检查浏览器是否支持Service Worker
if ("serviceWorker" in navigator) {
    // 先申请通知权限
    Notification.requestPermission().then(permission => {
        if (permission === "granted") {
            initServiceWorker().catch(err => console.error("初始化失败:", err));
        } else {
            console.warn("用户已拒绝通知权限");
        }
    });
}

// 注册Service Worker、订阅推送、注册后台同步
async function initServiceWorker() {
    // 注册Service Worker,注意路径修正为正确的文件地址
    console.log("正在注册Service Worker...");
    const register = await navigator.serviceWorker.register(
        "/service-worker.js",
        { scope: "/" }
    );
    console.log("Service Worker注册完成...");

    // 订阅推送服务
    console.log("正在注册推送服务...");
    const subscription = await register.pushManager.subscribe({
        userVisibleOnly: true, // 要求所有推送都对用户可见
        applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
    });
    console.log("推送服务注册完成...");

    // 将订阅信息发送到服务器保存
    console.log("正在同步订阅信息到服务器...");
    await fetch("/user/receive-notification", {
        method: "POST",
        body: JSON.stringify({ subscription }),
        headers: {
            Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2M2NlMDk4YTU0YTIyMDZhMzRhYzQzMjYiLCJlbWFpbCI6ImVtYWlsQGdtYWlsLmNvbSIsInZlcmlmaWVkIjpmYWxzZSwiaWF0IjoxNjc1MDc0MDk3fQ.zkvnahJh4uUlCRRxheE_24F5m55VLQWR1L04YvDO0ww",
            "Content-Type": "application/json"
        }
    });

    // 注册后台同步事件,标签页关闭后仍会执行
    await register.sync.register("fetch-data");
}

// 将Base64格式的VAPID密钥转换为Uint8Array
function urlBase64ToUint8Array(base64String) {
    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding)
        .replace(/-/g, "+")
        .replace(/_/g, "/");

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}

关键注意事项

  • 必须在HTTPS环境下运行(localhost可用于开发测试)
  • Service Worker文件需放在网站根目录,确保作用域覆盖整个站点
  • 后台同步功能依赖浏览器支持,部分移动端浏览器可能存在限制
  • 服务器需妥善保存用户的推送订阅信息,在需要时(如用户离线后有新数据)主动发起推送请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:25:31