你可能不需要 Service Worker

Lobsters Hottest 新闻

摘要

文章质疑了许多 Web 应用中对 Service Worker 的必要性,认为简单的 HTTP 缓存通常就足够了,而 Service Worker 会带来复杂性和缓存过期的风险。

<p><a href="https://lobste.rs/s/xgu1dh/you_might_not_need_service_worker">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/29 04:23

# 你可能不需要……一个 Service Worker 来源:https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/ 一个等距的浏览器路由,绕过空闲的 service worker 缓存机器,直接连接服务器资源。 Neciu 最近分析了一些 service worker 的有趣用例(https://neciudan.dev/)。我读到下面这段时确实有“被戳中”的感觉: > 在我的调查中,那两位“2019 年尝试过又移除”的人,讲了同一个故事,只是细节不同:一个采用错误缓存策略的 service worker 向用户提供了过时的应用,而且修复方法必须发布一个杀手级 worker,然后等好几天客户端才能接收到,因为那个有问题的 worker 控制了何时检查更新。 当年 service worker 刚推出时,我是早期采用者,并且很快就因为类似场景搬起石头砸了自己的脚。 我们来逐一分析那篇文章中的几个例子(如果你还没读过,建议先读原文(https://neciudan.dev/why-are-we-not-using-service-workers))。 ## 实际使用场景 https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#use-cases-in-the-wild ### Slack 的即时启动 https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#slacks-instant-boot 文章中最引人注目的例子是 Slack:缓存完整的资源集并重新水合 Redux 状态,使得 UI 在第一个网络请求完成之前就能渲染。 不过,关于资源部分的说法感觉有点夸张: > 他们观察到,两次启动之间,那份资源集几乎没有任何变化。周二早上打开 Slack 的用户下载的 JavaScript 与周一早上下载的完全一样。 HTTP 缓存应该足以缓解这个问题,而且实现起来简单得多。对于未变化的资源,内容哈希加上 `Cache-Control: public, max-age=31536000, immutable` 意味着它们应该直接从缓存中获取。 但这并不能实现无需网络的启动:你仍然需要获取 HTML 和任何先决数据。我认为这更多是一个“我是否需要离线支持?”的问题。对于 Slack 来说当然需要,但对于许多应用来说,可能并不需要。 如果你只是想避免重复下载相同的资源,只需给它们打上哈希,然后利用本地缓存即可。 ### 跨部署保持已废弃的代码块存活 https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#keeping-dead-chunks-alive-across-deploys 这是一个有趣的情况。一些供应商,比如 Vercel,提供了“偏斜保护”(https://vercel.com/docs/skew-protection),但我们大多数人都遇到过这个问题:客户端上的旧 bundle 在引用的资源不再存在时返回了 `404`。 你发布得越频繁,这个问题就越常见(如果你实行真正的 CI,可能每天发布数百次)。 Neciu 的解决方案是使用 service worker 在本地缓存应用。然而,这意味着要在后台缓存*所有*内容: ```jsonc { "version": "2026.06.04-1412", // 这要列到什么时候? "assets": ["/assets/index-c91d44.js", "/assets/Settings-c91d44.js"] } ``` 在我看来,这违背了路由/代码分割的初衷。当然,你会得到更快的初始渲染,但这也意味着每次失效都会强制客户端重新获取*整个*应用。对于我参与过的绝大多数应用来说,这会导致一个巨大且大部分都是浪费的负载。我们无法确定用户会访问哪些组件/页面,所以理论上我们需要下拉整个清单的内容。 如果我们只是保留静态资源一段时间(宽限期)呢?与其直接删除它们,不如让它们继续存在于存储桶中。使用内容哈希的文件名,部署永远不会覆盖任何内容:`Settings-a3f8b2.js` 和 `Settings-c91d44.js` 可以共存。 由于 service worker 并不能在后台无限期运行,核心的重新获取逻辑仍然必须存在于主应用中: > 页面驱动轮询,在间隔时间和 `visibilitychange` 事件上发送 `CHECK_VERSION`,因此一个在后台待了一整个周末的标签页会在回到前台时立即检查。 所以这也不需要 service worker。 ### Mux 的清单重写 https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#muxs-manifest-rewriting 这个很巧妙,但感觉它不应该存在于客户端。文章中提到的 bug 实际上是逻辑放在客户端所导致的*症状*: > 视频播放器在挂载的那一刻就开始获取资源,那时同页面的 worker 尚未取得控制权,因此他们不得不在索引页面注册 worker,然后链接到播放页面。 相反,应该将重写逻辑移到服务器端,这样更健壮,也更容易测试。只有清单(一个文本文件)需要重写,所以不用担心将大量视频数据通过额外的基础设施层。 文章中也提到了这一点: > ……因为像 Cloudflare Workers 这样的边缘运行时实现了相同的 fetch 事件 API,所以他们把拼接 worker 原封不动地部署到了 Cloudflare,并且得到了一个可用的 URL。 ### Partytown https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#partytown 这是一个很好的例子,但值得注意的是,service worker 版本实际上是回退方案: > Partytown 会在浏览器支持 Atomics 和 SharedArrayBuffer 时使用它们。 不幸的是,`SharedArrayBuffer` 仅在跨域隔离下才能工作,而这些标头往往会破坏第三方嵌入内容。因此实际上 service worker 回退的使用频率比你想象的要高,但它更像是一个逃生舱。 ### Mock Service Worker https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#mock-service-worker 这取决于你在构建什么,但随着转向服务端驱动的渲染策略和数据加载,你很可能使用的是 `setupServer`(它会修补 Node 内部)。 只有传统的 SPA 才会最终使用字面意义上的 service worker,尽管这个库的名字如此。 ## 那么你真的需要 service worker 吗? https://www.jayfreestone.com/writing/you-might-not-need-a-service-worker/#so-do-you-need-a-service-worker 你可以在 service worker 中做*很多*很酷的事情。也有一些事情只有 service worker 才能做到:离线支持、推送通知和后台同步是没有真正替代方案的。 但除此之外,我还没有遇到一个问题是 service worker 真正的最佳解决方案。 你有好的例子吗?告诉我吧。我一直在寻找一个重新审视它们的好借口。

相似文章

不要自己造轮子…

Lobsters Hottest

作者将“不要自己造轮子”的原则扩展到Web开发领域,反对自定义实现滚动、链接导航、文本选择等浏览器原生行为。

简单HTML的超凡有效性

Hacker News Top

一篇博文,论证了简单HTML对于普遍可访问性的持续重要性,并通过一位女性使用PSP浏览器访问政府服务的故事加以说明。

你很可能不需要 Yocto,这完全没问题

Lobsters Hottest

本文指出,对于嵌入式 Linux 项目,Yocto 常常过于复杂,建议开发者考虑更简单的替代方案,以避免维护负担,尤其是在 CRA 等法规下。

计算机应该如何工作

Lobsters Hottest

本文主张一种离线优先、简单的计算机设备,开机安静,无需互联网登录,让用户拥有应用程序,升级不擦除数据,轻松设置服务器,支持旧硬件,旨在不打扰用户,让用户创造。