{"id":272,"date":"2026-10-02T11:07:00","date_gmt":"2026-10-02T03:07:00","guid":{"rendered":"https:\/\/yykcj.com\/?p=272"},"modified":"2026-10-02T11:07:00","modified_gmt":"2026-10-02T03:07:00","slug":"service-worker-%e4%b8%8e-pwa-%e5%ae%8c%e6%95%b4%e6%8c%87%e5%8d%97%ef%bc%9a%e4%bb%8e%e7%a6%bb%e7%ba%bf%e7%bc%93%e5%ad%98%e5%88%b0%e6%b6%88%e6%81%af%e6%8e%a8%e9%80%81%e5%ae%9e%e6%88%98","status":"publish","type":"post","link":"https:\/\/yykcj.com\/?p=272","title":{"rendered":"Service Worker \u4e0e PWA \u5b8c\u6574\u6307\u5357\uff1a\u4ece\u79bb\u7ebf\u7f13\u5b58\u5230\u6d88\u606f\u63a8\u9001\u5b9e\u6218"},"content":{"rendered":"<h2>\u4e3a\u4ec0\u4e48\u4f60\u7684\u4e0b\u4e00\u4e2a\u9879\u76ee\u9700\u8981 PWA<\/h2>\n<p>\u5728\u79fb\u52a8\u4f18\u5148\u7684\u65f6\u4ee3\uff0c\u7528\u6237\u671f\u671b\u5e94\u7528\u80fd\u50cf\u539f\u751f\u4e00\u6837\u5feb\u901f\u3001\u53ef\u9760\u3001\u53ef\u5b89\u88c5\u3002PWA\uff08\u6e10\u8fdb\u5f0f Web \u5e94\u7528\uff09\u901a\u8fc7 Service Worker \u8d4b\u4e88 Web \u5e94\u7528\u79bb\u7ebf\u80fd\u529b\u3001\u540e\u53f0\u540c\u6b65\u548c\u63a8\u9001\u901a\u77e5\uff0c\u800c\u65e0\u9700\u4e0a\u67b6\u5e94\u7528\u5546\u5e97\u3002<\/p>\n<p>\u4f46\u5f88\u591a\u5f00\u53d1\u8005\u5bf9 Service Worker \u7684\u7406\u89e3\u505c\u7559\u5728\u201c\u7f13\u5b58 HTML\u201d\u7684\u5c42\u9762\uff0c\u5b9e\u9645\u843d\u5730\u65f6\u4f1a\u9047\u5230\u7f13\u5b58\u66f4\u65b0\u4e0d\u53ca\u65f6\u3001\u63a8\u9001\u6743\u9650\u88ab\u62d2\u3001\u540e\u53f0\u540c\u6b65\u5931\u8d25\u7b49\u95ee\u9898\u3002\u672c\u6587\u5c06\u7528\u4e00\u4e2a\u53ef\u8fd0\u884c\u7684 Demo\uff08\u4e00\u4e2a\u7b80\u5355\u7684\u7b14\u8bb0\u5e94\u7528\uff09\u5e26\u4f60\u5b8c\u6574\u8d70\u4e00\u904d PWA \u7684\u5b9e\u73b0\u8def\u5f84\uff0c\u5e76\u5206\u4eab\u6211\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u603b\u7ed3\u7684 7 \u4e2a\u6700\u4f73\u5b9e\u8df5\u3002<\/p>\n<h2>\u73af\u5883\u51c6\u5907\u4e0e\u9879\u76ee\u521d\u59cb\u5316<\/h2>\n<p>\u6211\u4eec\u4f7f\u7528\u539f\u751f JavaScript + Vite \u6784\u5efa\uff0c\u907f\u514d\u6846\u67b6\u5e72\u6270\u3002\u5148\u521b\u5efa\u9879\u76ee\uff1a<\/p>\n<pre><code class=\"language-bash\">\nnpm create vite@latest pwa-notes -- --template vanilla\ncd pwa-notes\nnpm install\nnpm install workbox-window -D\n<\/code><\/pre>\n<p>\u9879\u76ee\u7ed3\u6784\u5982\u4e0b\uff1a<\/p>\n<pre><code>\npwa-notes\/\n\u251c\u2500\u2500 index.html\n\u251c\u2500\u2500 main.js\n\u251c\u2500\u2500 sw.js          # Service Worker \u5165\u53e3\n\u251c\u2500\u2500 manifest.json  # PWA \u6e05\u5355\u6587\u4ef6\n\u2514\u2500\u2500 icons\/         # \u5e94\u7528\u56fe\u6807\n<\/code><\/pre>\n<h2>\u7b2c\u4e00\u6b65\uff1a\u7f16\u5199 manifest.json \u8ba9\u5e94\u7528\u53ef\u5b89\u88c5<\/h2>\n<p>PWA \u53ef\u5b89\u88c5\u7684\u524d\u63d0\u662f\u63d0\u4f9b\u6709\u6548\u7684 manifest\u3002\u5728\u9879\u76ee\u6839\u76ee\u5f55\u521b\u5efa <code>manifest.json<\/code>\uff1a<\/p>\n<pre><code class=\"language-json\">\n{\n  &quot;name&quot;: &quot;PWA Notes&quot;,\n  &quot;short_name&quot;: &quot;Notes&quot;,\n  &quot;start_url&quot;: &quot;\/&quot;,\n  &quot;display&quot;: &quot;standalone&quot;,\n  &quot;background_color&quot;: &quot;#ffffff&quot;,\n  &quot;theme_color&quot;: &quot;#4f46e5&quot;,\n  &quot;icons&quot;: [\n    {\n      &quot;src&quot;: &quot;\/icons\/icon-192.png&quot;,\n      &quot;sizes&quot;: &quot;192x192&quot;,\n      &quot;type&quot;: &quot;image\/png&quot;\n    },\n    {\n      &quot;src&quot;: &quot;\/icons\/icon-512.png&quot;,\n      &quot;sizes&quot;: &quot;512x512&quot;,\n      &quot;type&quot;: &quot;image\/png&quot;\n    }\n  ]\n}\n<\/code><\/pre>\n<p>\u5728 <code>index.html<\/code> \u7684 <code><head><\/code> \u4e2d\u5f15\u5165\uff1a<\/p>\n<pre><code class=\"language-html\">\n&lt;link rel=&quot;manifest&quot; href=&quot;\/manifest.json&quot; \/&gt;\n&lt;meta name=&quot;theme-color&quot; content=&quot;#4f46e5&quot; \/&gt;\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1a<code>start_url<\/code> \u5fc5\u987b\u662f\u76f8\u5bf9\u8def\u5f84\u6216\u540c\u6e90\u8def\u5f84\uff0c\u5426\u5219\u5b89\u88c5\u63d0\u793a\u4e0d\u4f1a\u51fa\u73b0\u3002\u56fe\u6807\u81f3\u5c11\u63d0\u4f9b 192&#215;192 \u548c 512&#215;512 \u4e24\u4e2a\u5c3a\u5bf8\uff0cChrome \u624d\u4f1a\u89e6\u53d1\u5b89\u88c5\u6a2a\u5e45\u3002<\/p>\n<h2>\u7b2c\u4e8c\u6b65\uff1aService Worker \u751f\u547d\u5468\u671f\u4e0e\u6ce8\u518c<\/h2>\n<p>Service Worker \u7684\u751f\u547d\u5468\u671f\u662f\u7406\u89e3\u4e00\u5207\u7f13\u5b58\u884c\u4e3a\u7684\u57fa\u7840\u3002\u5b83\u7ecf\u5386 <code>install<\/code> \u2192 <code>waiting<\/code> \u2192 <code>activate<\/code> \u2192 <code>fetch<\/code> \u7b49\u9636\u6bb5\u3002\u5173\u952e\u70b9\uff1a\u65b0\u7684 SW \u4e0d\u4f1a\u7acb\u5373\u63a5\u7ba1\u9875\u9762\uff0c\u76f4\u5230\u6240\u6709\u65e7\u9875\u9762\u5173\u95ed\u3002<\/p>\n<p>\u5728 <code>main.js<\/code> \u4e2d\u6ce8\u518c\uff1a<\/p>\n<pre><code class=\"language-javascript\">\nif (&#x27;serviceWorker&#x27; in navigator) {\n  window.addEventListener(&#x27;load&#x27;, async () =&gt; {\n    try {\n      const registration = await navigator.serviceWorker.register(&#x27;\/sw.js&#x27;, {\n        scope: &#x27;\/&#x27;\n      });\n      console.log(&#x27;SW \u6ce8\u518c\u6210\u529f\uff0c\u4f5c\u7528\u57df:&#x27;, registration.scope);\n    } catch (error) {\n      console.error(&#x27;SW \u6ce8\u518c\u5931\u8d25:&#x27;, error);\n    }\n  });\n}\n<\/code><\/pre>\n<p><strong>\ud83d\udca1 \u6700\u4f73\u5b9e\u8df5<\/strong>\uff1a\u4e0d\u8981\u7528 <code>navigator.serviceWorker.ready<\/code> \u6765\u7b49\u5f85\u6fc0\u6d3b\uff0c\u5b83\u6c38\u8fdc\u4e0d\u4f1a\u5728\u6ce8\u518c\u5931\u8d25\u65f6 resolve\u3002\u66f4\u597d\u7684\u505a\u6cd5\u662f\u76d1\u542c <code>updatefound<\/code> \u4e8b\u4ef6\uff0c\u63d0\u793a\u7528\u6237\u5237\u65b0\u3002<\/p>\n<h2>\u7b2c\u4e09\u6b65\uff1a\u5b9e\u73b0\u79bb\u7ebf\u7f13\u5b58\u7b56\u7565<\/h2>\n<p>\u5728 <code>sw.js<\/code> \u4e2d\uff0c\u6211\u4eec\u91c7\u7528 <strong>Stale-While-Revalidate<\/strong> \u7b56\u7565\uff1a\u4f18\u5148\u8fd4\u56de\u7f13\u5b58\uff0c\u540c\u65f6\u540e\u53f0\u66f4\u65b0\u7f13\u5b58\u3002\u8fd9\u662f\u5e73\u8861\u901f\u5ea6\u548c\u65b0\u9c9c\u5ea6\u7684\u6700\u4f73\u65b9\u6848\u3002<\/p>\n<pre><code class=\"language-javascript\">\nconst CACHE_NAME = &#x27;pwa-notes-v1&#x27;;\nconst PRECACHE_URLS = [&#x27;\/&#x27;, &#x27;\/index.html&#x27;, &#x27;\/main.js&#x27;, &#x27;\/manifest.json&#x27;];\n\n\/\/ \u5b89\u88c5\u65f6\u9884\u7f13\u5b58\u6838\u5fc3\u8d44\u6e90\nself.addEventListener(&#x27;install&#x27;, (event) =&gt; {\n  event.waitUntil(\n    caches.open(CACHE_NAME).then((cache) =&gt; cache.addAll(PRECACHE_URLS))\n  );\n  self.skipWaiting(); \/\/ \u5f3a\u5236\u65b0 SW \u7acb\u5373\u6fc0\u6d3b\n});\n\n\/\/ \u6fc0\u6d3b\u65f6\u6e05\u7406\u65e7\u7f13\u5b58\nself.addEventListener(&#x27;activate&#x27;, (event) =&gt; {\n  event.waitUntil(\n    caches.keys().then((keys) =&gt;\n      Promise.all(\n        keys.filter((key) =&gt; key !== CACHE_NAME).map((key) =&gt; caches.delete(key))\n      )\n    )\n  );\n  self.clients.claim(); \/\/ \u7acb\u5373\u63a5\u7ba1\u6240\u6709\u9875\u9762\n});\n\n\/\/ \u62e6\u622a\u8bf7\u6c42\nself.addEventListener(&#x27;fetch&#x27;, (event) =&gt; {\n  const { request } = event;\n  \/\/ \u53ea\u5904\u7406 GET \u8bf7\u6c42\n  if (request.method !== &#x27;GET&#x27;) return;\n\n  event.respondWith(\n    caches.match(request).then((cached) =&gt; {\n      const fetchPromise = fetch(request)\n        .then((networkResponse) =&gt; {\n          \/\/ \u53ea\u7f13\u5b58\u6210\u529f\u7684\u54cd\u5e94\n          if (networkResponse &amp;&amp; networkResponse.status === 200) {\n            const responseClone = networkResponse.clone();\n            caches.open(CACHE_NAME).then((cache) =&gt; {\n              cache.put(request, responseClone);\n            });\n          }\n          return networkResponse;\n        })\n        .catch(() =&gt; cached); \/\/ \u7f51\u7edc\u5931\u8d25\u65f6\u56de\u9000\u5230\u7f13\u5b58\n\n      return cached || fetchPromise;\n    })\n  );\n});\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1a<code>skipWaiting()<\/code> \u548c <code>clients.claim()<\/code> \u867d\u7136\u65b9\u4fbf\uff0c\u4f46\u53ef\u80fd\u5bfc\u81f4\u65b0\u65e7\u7248\u672c\u8d44\u6e90\u6df7\u7528\u3002\u751f\u4ea7\u73af\u5883\u5efa\u8bae\u914d\u5408\u7248\u672c\u53f7\u63a7\u5236\uff0c\u6216\u4f7f\u7528 Workbox \u7684 <code>GenerateSW<\/code> \u63d2\u4ef6\u81ea\u52a8\u5904\u7406\u3002<\/p>\n<h2>\u7b2c\u56db\u6b65\uff1a\u540e\u53f0\u540c\u6b65\uff08Background Sync\uff09<\/h2>\n<p>\u5f53\u7528\u6237\u79bb\u7ebf\u65f6\u63d0\u4ea4\u7b14\u8bb0\uff0c\u6211\u4eec\u5e0c\u671b\u7f51\u7edc\u6062\u590d\u540e\u81ea\u52a8\u91cd\u8bd5\u3002\u540e\u53f0\u540c\u6b65 API \u53ef\u4ee5\u505a\u5230\uff1a<\/p>\n<pre><code class=\"language-javascript\">\n\/\/ main.js \u4e2d\u63d0\u4ea4\u8868\u5355\nasync function submitNote(note) {\n  try {\n    const response = await fetch(&#x27;\/api\/notes&#x27;, {\n      method: &#x27;POST&#x27;,\n      body: JSON.stringify(note),\n      headers: { &#x27;Content-Type&#x27;: &#x27;application\/json&#x27; }\n    });\n    if (!response.ok) throw new Error(&#x27;\u63d0\u4ea4\u5931\u8d25&#x27;);\n  } catch (error) {\n    \/\/ \u79bb\u7ebf\u6216\u5931\u8d25\uff0c\u6ce8\u518c\u540e\u53f0\u540c\u6b65\n    const registration = await navigator.serviceWorker.ready;\n    await registration.sync.register(&#x27;sync-notes&#x27;);\n    \/\/ \u5c06\u6570\u636e\u5b58\u5165 IndexedDB \u4ee5\u4fbf\u540e\u7eed\u91cd\u8bd5\n    await saveNoteToIndexedDB(note);\n  }\n}\n<\/code><\/pre>\n<p>\u5728 <code>sw.js<\/code> \u4e2d\u76d1\u542c\u540c\u6b65\u4e8b\u4ef6\uff1a<\/p>\n<pre><code class=\"language-javascript\">\nself.addEventListener(&#x27;sync&#x27;, (event) =&gt; {\n  if (event.tag === &#x27;sync-notes&#x27;) {\n    event.waitUntil(retryPendingNotes());\n  }\n});\n\nasync function retryPendingNotes() {\n  const notes = await getPendingNotesFromIndexedDB();\n  for (const note of notes) {\n    try {\n      await fetch(&#x27;\/api\/notes&#x27;, {\n        method: &#x27;POST&#x27;,\n        body: JSON.stringify(note),\n        headers: { &#x27;Content-Type&#x27;: &#x27;application\/json&#x27; }\n      });\n      await deleteNoteFromIndexedDB(note.id);\n    } catch (error) {\n      \/\/ \u4ecd\u7136\u5931\u8d25\uff0c\u7b49\u5f85\u4e0b\u6b21\u540c\u6b65\n      throw error;\n    }\n  }\n}\n<\/code><\/pre>\n<p><strong>\ud83d\udca1 \u6700\u4f73\u5b9e\u8df5<\/strong>\uff1a\u540e\u53f0\u540c\u6b65\u53ea\u5728\u7528\u6237\u6709\u7f51\u7edc\u65f6\u89e6\u53d1\uff0c\u4f46 Chrome \u8981\u6c42\u9875\u9762\u5728\u540e\u53f0\u65f6\u624d\u80fd\u89e6\u53d1\u540c\u6b65\u3002\u6d4b\u8bd5\u65f6\u53ef\u4ee5\u901a\u8fc7 DevTools \u7684 Application \u2192 Background Services \u2192 Sync \u624b\u52a8\u89e6\u53d1\u3002<\/p>\n<h2>\u7b2c\u4e94\u6b65\uff1a\u6d88\u606f\u63a8\u9001\uff08Push API\uff09<\/h2>\n<p>\u63a8\u9001\u9700\u8981\u540e\u7aef\u914d\u5408\uff0c\u8fd9\u91cc\u6211\u4eec\u7528 Node.js \u6f14\u793a\u4e00\u4e2a\u6781\u7b80\u7684\u63a8\u9001\u670d\u52a1\u3002\u5148\u5728\u524d\u7aef\u8ba2\u9605\uff1a<\/p>\n<pre><code class=\"language-javascript\">\n\/\/ main.js \u4e2d\u8bf7\u6c42\u901a\u77e5\u6743\u9650\u5e76\u8ba2\u9605\nasync function subscribePush() {\n  const permission = await Notification.requestPermission();\n  if (permission !== &#x27;granted&#x27;) return;\n\n  const registration = await navigator.serviceWorker.ready;\n  const subscription = await registration.pushManager.subscribe({\n    userVisibleOnly: true,\n    applicationServerKey: urlBase64ToUint8Array(&#x27;YOUR_PUBLIC_VAPID_KEY&#x27;)\n  });\n\n  \/\/ \u5c06 subscription \u53d1\u9001\u5230\u540e\u7aef\u4fdd\u5b58\n  await fetch(&#x27;\/api\/subscribe&#x27;, {\n    method: &#x27;POST&#x27;,\n    body: JSON.stringify(subscription),\n    headers: { &#x27;Content-Type&#x27;: &#x27;application\/json&#x27; }\n  });\n}\n\nfunction urlBase64ToUint8Array(base64String) {\n  const padding = &#x27;=&#x27;.repeat((4 - (base64String.length % 4)) % 4);\n  const base64 = (base64String + padding).replace(\/-\/g, &#x27;+&#x27;).replace(\/_\/g, &#x27;\/&#x27;);\n  const rawData = atob(base64);\n  return Uint8Array.from([...rawData].map((char) =&gt; char.charCodeAt(0)));\n}\n<\/code><\/pre>\n<p>\u5728 <code>sw.js<\/code> \u4e2d\u5904\u7406\u63a8\u9001\u4e8b\u4ef6\uff1a<\/p>\n<pre><code class=\"language-javascript\">\nself.addEventListener(&#x27;push&#x27;, (event) =&gt; {\n  const data = event.data ? event.data.json() : {};\n  event.waitUntil(\n    self.registration.showNotification(data.title || &#x27;\u65b0\u6d88\u606f&#x27;, {\n      body: data.body || &#x27;\u4f60\u6709\u4e00\u6761\u65b0\u901a\u77e5&#x27;,\n      icon: &#x27;\/icons\/icon-192.png&#x27;,\n      badge: &#x27;\/icons\/badge.png&#x27;,\n      data: { url: data.url || &#x27;\/&#x27; }\n    })\n  );\n});\n\nself.addEventListener(&#x27;notificationclick&#x27;, (event) =&gt; {\n  event.notification.close();\n  event.waitUntil(\n    clients.openWindow(event.notification.data.url)\n  );\n});\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1aVAPID \u5bc6\u94a5\u5bf9\u5fc5\u987b\u901a\u8fc7 <code>web-push<\/code> \u5e93\u751f\u6210\uff0c\u516c\u94a5\u5728\u524d\u7aef\u4f7f\u7528\uff0c\u79c1\u94a5\u5728\u540e\u7aef\u7b7e\u540d\u3002\u4e0d\u8981\u5c06\u79c1\u94a5\u66b4\u9732\u5728\u524d\u7aef\u4ee3\u7801\u4e2d\u3002<\/p>\n<h2>\u5e38\u89c1\u5751\u4e0e\u89e3\u51b3\u65b9\u6848<\/h2>\n<h3>1. \u7f13\u5b58\u4e86\u9519\u8bef\u7684\u54cd\u5e94<\/h3>\n<p><code>cache.put()<\/code> \u4f1a\u7f13\u5b58\u6240\u6709\u54cd\u5e94\uff0c\u5305\u62ec 404 \u548c 500\u3002\u52a1\u5fc5\u68c0\u67e5 <code>response.status === 200<\/code> \u4e14 <code>response.type === 'basic'<\/code>\uff08\u540c\u6e90\uff09\u3002<\/p>\n<h3>2. \u5f00\u53d1\u73af\u5883\u7f13\u5b58\u5bfc\u81f4\u70ed\u66f4\u65b0\u5931\u6548<\/h3>\n<p>\u5728\u5f00\u53d1\u73af\u5883\u4e2d\uff0c\u5efa\u8bae\u7981\u7528 Service Worker \u6216\u4f7f\u7528 <code>network-first<\/code> \u7b56\u7565\u3002Vite \u9879\u76ee\u53ef\u4ee5\u901a\u8fc7\u73af\u5883\u53d8\u91cf\u5224\u65ad\uff1a<\/p>\n<pre><code class=\"language-javascript\">\nif (import.meta.env.DEV) {\n  \/\/ \u5f00\u53d1\u73af\u5883\u4e0d\u6ce8\u518c SW\n} else {\n  navigator.serviceWorker.register(&#x27;\/sw.js&#x27;);\n}\n<\/code><\/pre>\n<h3>3. \u63a8\u9001\u6743\u9650\u88ab\u62d2\u7edd\u540e\u65e0\u6cd5\u518d\u6b21\u8bf7\u6c42<\/h3>\n<p>\u4e00\u65e6\u7528\u6237\u62d2\u7edd\uff0c<code>Notification.requestPermission()<\/code> \u4e0d\u4f1a\u518d\u5f39\u51fa\u3002\u5fc5\u987b\u5f15\u5bfc\u7528\u6237\u624b\u52a8\u5728\u6d4f\u89c8\u5668\u8bbe\u7f6e\u4e2d\u5f00\u542f\u3002<\/p>\n<h3>4. iOS Safari \u7684\u517c\u5bb9\u6027<\/h3>\n<p>iOS \u4ece 16.4 \u5f00\u59cb\u652f\u6301 Web Push\uff0c\u4f46\u8981\u6c42 PWA \u5fc5\u987b\u6dfb\u52a0\u5230\u4e3b\u5c4f\u5e55\u3002\u4e14\u4e0d\u652f\u6301 <code>beforeinstallprompt<\/code> \u4e8b\u4ef6\uff0c\u9700\u8981\u624b\u52a8\u5f15\u5bfc\u7528\u6237\u901a\u8fc7\u5206\u4eab\u83dc\u5355\u5b89\u88c5\u3002<\/p>\n<h2>\u4f7f\u7528 Workbox \u7b80\u5316\u751f\u4ea7\u73af\u5883<\/h2>\n<p>\u624b\u52a8\u7ba1\u7406\u7f13\u5b58\u7248\u672c\u548c\u7b56\u7565\u5bb9\u6613\u51fa\u9519\uff0c\u63a8\u8350\u4f7f\u7528 Workbox\u3002\u5b89\u88c5\uff1a<\/p>\n<pre><code class=\"language-bash\">\nnpm install workbox-webpack-plugin -D\n<\/code><\/pre>\n<p>\u5728 <code>vite.config.js<\/code> \u4e2d\u914d\u7f6e\uff1a<\/p>\n<pre><code class=\"language-javascript\">\nimport { VitePWA } from &#x27;vite-plugin-pwa&#x27;;\n\nexport default {\n  plugins: [\n    VitePWA({\n      registerType: &#x27;autoUpdate&#x27;,\n      workbox: {\n        globPatterns: [&#x27;**\/*.{js,css,html,png,svg}&#x27;],\n        runtimeCaching: [\n          {\n            urlPattern: \/^https:\\\/\\\/api\\.example\\.com\\\/.*\/i,\n            handler: &#x27;NetworkFirst&#x27;,\n            options: {\n              cacheName: &#x27;api-cache&#x27;,\n              expiration: { maxEntries: 50, maxAgeSeconds: 86400 }\n            }\n          }\n        ]\n      }\n    })\n  ]\n};\n<\/code><\/pre>\n<p>Workbox \u4f1a\u81ea\u52a8\u751f\u6210 <code>sw.js<\/code> \u5e76\u5904\u7406\u7248\u672c\u63a7\u5236\uff0c\u4f60\u53ea\u9700\u5173\u6ce8\u4e1a\u52a1\u903b\u8f91\u3002<\/p>\n<h2>\u603b\u7ed3\u4e0e\u4e0b\u4e00\u6b65<\/h2>\n<p>\u901a\u8fc7\u672c\u6587\uff0c\u4f60\u5b8c\u6210\u4e86\uff1a<\/p>\n<ul>\n<li>\u914d\u7f6e manifest \u4f7f\u5e94\u7528\u53ef\u5b89\u88c5<\/li>\n<li>\u5b9e\u73b0 Stale-While-Revalidate \u7f13\u5b58\u7b56\u7565<\/li>\n<li>\u4f7f\u7528 Background Sync \u5904\u7406\u79bb\u7ebf\u63d0\u4ea4<\/li>\n<li>\u96c6\u6210 Push API \u5b9e\u73b0\u6d88\u606f\u63a8\u9001<\/li>\n<li>\u4e86\u89e3\u5e38\u89c1\u5751\u4e0e Workbox \u6700\u4f73\u5b9e\u8df5<\/li>\n<\/ul>\n<p><strong>\u4e0b\u4e00\u6b65\u5efa\u8bae<\/strong>\uff1a<\/p>\n<ol>\n<li>\u5b66\u4e60 Workbox \u7684 <code>BroadcastUpdatePlugin<\/code> \u5b9e\u73b0\u9875\u9762\u65e0\u5237\u65b0\u66f4\u65b0<\/li>\n<li>\u63a2\u7d22 Periodic Background Sync \u5b9e\u73b0\u5b9a\u65f6\u540c\u6b65<\/li>\n<li>\u4f7f\u7528 Lighthouse \u5ba1\u8ba1 PWA \u5f97\u5206\uff0c\u4f18\u5316\u6027\u80fd\u6307\u6807<\/li>\n<\/ol>\n<p>PWA \u4e0d\u662f\u94f6\u5f39\uff0c\u4f46\u5b83\u662f\u76ee\u524d\u6700\u63a5\u8fd1\u539f\u751f\u4f53\u9a8c\u7684 Web \u65b9\u6848\u3002\u5e0c\u671b\u8fd9\u7bc7\u6307\u5357\u80fd\u5e2e\u4f60\u5c11\u8d70\u5f2f\u8def\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u624b\u628a\u624b\u5e26\u4f60\u5b9e\u73b0\u4e00\u4e2a\u5b8c\u6574\u7684 PWA \u5e94\u7528\uff0c\u6db5\u76d6 Service Worker \u751f\u547d\u5468\u671f\u3001\u79bb\u7ebf\u7f13\u5b58\u7b56\u7565\u3001\u540e\u53f0\u540c\u6b65\u4e0e\u6d88\u606f\u63a8\u9001\u3002\u4f60\u5c06\u638c\u63e1 Workbox \u6700\u4f73\u5b9e\u8df5\uff0c\u5e76\u907f\u5f00\u5e38\u89c1\u7684\u7f13\u5b58\u9677\u9631\u3002<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,4],"tags":[47,46,253,252,48],"class_list":["post-272","post","type-post","status-publish","format-standard","hentry","category-3","category-4","tag-pwa","tag-service-worker","tag-workbox","tag-252","tag-48"],"_links":{"self":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/272","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=272"}],"version-history":[{"count":0,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/272\/revisions"}],"wp:attachment":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=272"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=272"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=272"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}