正在加载,请稍候…

2026 年的 PWA:Service Worker、离线优先与现代安装体验

一份构建 2026 年渐进式 Web 应用的完整指南,涵盖高级 Service Worker 模式、离线优先策略、推送通知和无缝安装体验。

2026 年的 PWA:Service Worker、离线优先与现代安装体验

2026 年的 PWA:Service Worker、离线优先与现代安装体验

渐进式 Web 应用已经显著成熟。到 2026 年,PWA 在 Android 和 iOS 上都能提供与原生应用相媲美的能力。借助 Service Worker、后台同步、推送通知和文件系统访问,Web 与原生之间的界限持续模糊。

2026 年的 PWA 能力全景

现代 PWA 可以访问:

  • 文件系统访问 API(File System Access API):在用户许可下读写文件
  • Web Bluetooth 和 USB:连接硬件设备
  • 后台同步(Background Sync):在连接恢复时排队执行操作
  • 定期后台同步(Periodic Background Sync):定期更新数据(Android)
  • Web 推送(Web Push):即使应用关闭也能发送通知
  • 徽章 API(Badging API):应用图标徽章
  • 共享目标 API(Share Target API):接收来自其他应用的共享内容

2026 年的 PWA:Service Worker、离线优先与现代安装体验 插图

Web App Manifest 深入解析

{
  "name": "My Production PWA",
  "short_name": "MyPWA",
  "display": "standalone",
  "display_override": ["window-controls-overlay", "standalone", "browser"],
  "background_color": "#ffffff",
  "theme_color": "#6366f1",
  "screenshots": [
    {
      "src": "/screenshots/desktop.png",
      "sizes": "1280x800",
      "type": "image/png",
      "form_factor": "wide",
      "label": "Desktop view"
    }
  ],
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
  ],
  "file_handlers": [
    {
      "action": "/open-file",
      "accept": { "text/markdown": [".md"] }
    }
  ],
  "share_target": {
    "action": "/share-target",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": { "title": "title", "text": "text", "url": "url" }
  }
}

高级 Service Worker 模式

Workbox 7.x 配置

import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { BroadcastUpdatePlugin } from 'workbox-broadcast-update';

precacheAndRoute(self.__WB_MANIFEST);
cleanupOutdatedCaches();

// HTML - Network First
registerRoute(
  ({ request }) => request.destination === 'document',
  new NetworkFirst({
    cacheName: 'html-cache',
    networkTimeoutSeconds: 3,
    plugins: [new ExpirationPlugin({ maxAgeSeconds: 24 * 60 * 60 })],
  })
);

// API - Network First with update notification
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache',
    networkTimeoutSeconds: 5,
    plugins: [
      new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 5 * 60 }),
      new BroadcastUpdatePlugin(),
    ],
  })
);

// Images - Cache First, long expiry
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new ExpirationPlugin({ maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 }),
    ],
  })
);

2026 年的 PWA:Service Worker、离线优先与现代安装体验 插图

后台同步

const bgSyncPlugin = new BackgroundSyncPlugin('form-queue', {
  maxRetentionTime: 24 * 60,
  onSync: async ({ queue }) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        await fetch(entry.request);
      } catch (error) {
        await queue.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

registerRoute(
  ({ url }) => url.pathname === '/api/submit',
  new NetworkOnly({ plugins: [bgSyncPlugin] }),
  'POST'
);

智能更新管理

class ServiceWorkerManager {
  async register() {
    if (!('serviceWorker' in navigator)) return;

    this.registration = await navigator.serviceWorker.register('/sw.js', {
      scope: '/',
      updateViaCache: 'none',
    });

    setInterval(() => this.registration.update(), 60 * 60 * 1000);

    this.registration.addEventListener('updatefound', () => {
      const newWorker = this.registration.installing;
      newWorker.addEventListener('statechange', () => {
        if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
          this.onUpdateAvailable?.();
        }
      });
    });

    navigator.serviceWorker.addEventListener('controllerchange', () => {
      window.location.reload();
    });
  }

  skipWaiting() {
    this.registration?.waiting?.postMessage({ type: 'SKIP_WAITING' });
  }
}

推送通知

2026 年的 PWA:Service Worker、离线优先与现代安装体验 插图

请求权限并订阅

async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready;
  const permission = await Notification.requestPermission();
  if (permission !== 'granted') throw new Error('Permission denied');

  const sub = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY),
  });

  await fetch('/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(sub.toJSON()),
  });

  return sub;
}

在 Service Worker 中处理推送

self.addEventListener('push', (event) => {
  const data = event.data?.json() ?? {};
  event.waitUntil(
    self.registration.showNotification(data.title ?? 'New Update', {
      body: data.body,
      icon: '/icons/icon-192.png',
      badge: '/icons/badge-72.png',
      data: { url: data.url },
      actions: [
        { action: 'open', title: 'Open App' },
        { action: 'dismiss', title: 'Dismiss' },
      ],
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  if (event.action === 'dismiss') return;

  const url = event.notification.data?.url ?? '/';
  event.waitUntil(
    clients.matchAll({ type: 'window' }).then((clientList) => {
      const existing = clientList.find(c => c.url === url);
      if (existing) return existing.focus();
      return clients.openWindow(url);
    })
  );
});

自定义安装提示

class PWAInstallManager {
  private deferredPrompt = null;

  constructor() {
    window.addEventListener('beforeinstallprompt', (e) => {
      e.preventDefault();
      this.deferredPrompt = e;
      this.onInstallAvailable?.();
    });

    window.addEventListener('appinstalled', () => {
      this.deferredPrompt = null;
      analytics.track('pwa_installed');
    });
  }

  get canInstall() { return !!this.deferredPrompt; }

  async promptInstall() {
    if (!this.deferredPrompt) return null;
    this.deferredPrompt.prompt();
    const { outcome } = await this.deferredPrompt.userChoice;
    this.deferredPrompt = null;
    return outcome;
  }
}

定期后台同步

// Register periodic sync
async function registerPeriodicSync() {
  const registration = await navigator.serviceWorker.ready;
  if ('periodicSync' in registration) {
    try {
      await registration.periodicSync.register('update-feed', {
        minInterval: 24 * 60 * 60 * 1000, // 24 hours
      });
    } catch (e) {
      console.log('Periodic sync registration failed:', e);
    }
  }
}

// Handle in service worker
self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'update-feed') {
    event.waitUntil(updateFeedCache());
  }
});

结论

2026 年的 PWA 是真正具备跨平台能力的应用。通过高级 Service Worker 策略、后台同步、推送通知和精心设计的安装体验,你可以通过 Web 提供应用商店级别的体验。对 PWA 架构的投入将在所有平台上同时获得回报。