mcpField
P
pwa-development

pwa-development

github.com/mindrally开发289785 次安装3 周前更新
代码仓库

关于 pwa-development

pwa-development 提供离线优先 Progressive Web Apps 的开发指南与示例,涵盖清单、Service Worker、缓存、同步、通知、性能和测试。

主要功能

  1. 01离线优先设计指南
  2. 02清单与 Service Worker 配置
  3. 03缓存策略示例
  4. 04后台同步与推送通知
  5. 05性能和测试建议

适用场景

  1. 01规划支持离线的 PWA
  2. 02为静态和动态内容选择缓存策略
  3. 03添加后台同步或通知
  4. 04检查 PWA 性能和离线行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill pwa-development

安装后在 Claude Code 中输入/pwa-development确认已加载。

详细介绍

渐进式 Web 应用开发指南

你是构建具备离线优先能力的渐进式 Web 应用方面的专家。

核心原则

  • 设计离线优先的体验
  • 实施恰当的缓存策略
  • 确保快速加载和流畅性能
  • 遵循 Web 应用清单最佳实践
  • 提供类似原生应用的体验

Web 应用清单

{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "A description of your app",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Service Worker 注册

// Register service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js');
      console.log('SW registered:', registration.scope);
    } catch (error) {
      console.error('SW registration failed:', error);
    }
  });
}

Service Worker 实现

// sw.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/offline.html'
];

// Install event - cache static assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(STATIC_ASSETS);
    })
  );
  self.skipWaiting();
});

// Activate event - cleanup old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    })
  );
  self.clients.claim();
});

// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    }).catch(() => {
      return caches.match('/offline.html');
    })
  );
});

缓存策略

缓存优先(静态资源)
async function cacheFirst(request) {
  const cached = await caches.match(request);
  return cached || fetch(request);
}
网络优先(动态内容)
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}
先返回缓存,再重新验证
async function staleWhileRevalidate(request) {
  const cache = await caches.open(CACHE_NAME);
  const cached = await cache.match(request);

  const fetchPromise = fetch(request).then((response) => {
    cache.put(request, response.clone());
    return response;
  });

  return cached || fetchPromise;
}

后台同步

// Register sync in main app
async function registerSync() {
  const registration = await navigator.serviceWorker.ready;
  await registration.sync.register('sync-data');
}

// Handle sync in service worker
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-data') {
    event.waitUntil(syncData());
  }
});

async function syncData() {
  const data = await getQueuedData();
  await fetch('/api/sync', {
    method: 'POST',
    body: JSON.stringify(data)
  });
}

推送通知

// Request permission
async function requestNotificationPermission() {
  const permission = await Notification.requestPermission();
  if (permission === 'granted') {
    const registration = await navigator.serviceWorker.ready;
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: PUBLIC_VAPID_KEY
    });
    // Send subscription to server
  }
}

// Handle push in service worker
self.addEventListener('push', (event) => {
  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png'
    })
  );
});

离线检测

// Check online status
window.addEventListener('online', () => {
  console.log('Back online');
  syncPendingData();
});

window.addEventListener('offline', () => {
  console.log('Offline mode');
  showOfflineIndicator();
});

性能优化

  • 实施应用外壳架构
  • 对路由和组件使用懒加载
  • 使用响应式格式优化图片
  • 尽量减小 JavaScript 包的大小
  • 使用代码拆分

测试

  • 测试离线功能
  • 验证缓存行为
  • 在各种网络条件下进行测试
  • 验证清单和图标
  • 使用 Lighthouse 执行 PWA 审计

最佳实践

  • 通过 HTTPS 提供服务
  • 提供有意义的离线体验
  • 妥善处理 Service Worker 更新
  • 实施恰当的错误处理
  • 添加加载状态和骨架屏

评论

支持 Markdown