# 把 PWA 首页从 4.8 MB 压到 70 KB

> 一篇 dev.to 报告把一个 PDF 工具站的落地页压到约 70 KB，并重新调整 Service Worker，让预缓存装的是应用外壳而不是那些库。

2026-10-09，dev.to 账号 Shahzaib 发表了一篇关于 PdfWord 的报告。PdfWord 是一个 PDF 工具站，
文章把它描述为免费、无需注册、纯客户端的，并写明已安装的 PWA 在手机上打开要花很长时间，用户
为此抱怨。文章把随后发生的事称为一次削掉了 98.5% 重量的 20 分钟修复，而它覆盖两处彼此独立的
改动：落地页下载什么，以及 Service Worker 在安装时预缓存什么
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

## 这次测量发现了什么

文章的第一步是一次测量，而不是一次优化。它写明在任何改动作出之前，作者测量了首页实际下载了
什么
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

### 文章列出的库体积

文章的表格给出 `html2pdf.bundle.min.js` 为 884 KB，`xlsx.full.min.js` 为 861 KB，
`docx.min.js` 为 725 KB，`pdf-lib.min.js` 为 512 KB，`pptxgen.bundle.js` 为 466 KB，
`jspdf.umd.min.js` 为 410 KB，`pdf.min.js` 为 312 KB，后面跟着一行写着
"plus 25 more scripts"。文章在那张表下面打印的总数，是一个落地页上 4.8 MB 的 JavaScript
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

### 首页实际需要什么

文章对这个页面职责的叙述是：首页只是一些链接到各个工具页的卡片，而那张表里的库它一个也不
需要。它对这些库为何在场的解释是，每个库都是“以防万一”被加载进来的；它还补充说，这个页面
同时还带着 22 个来自旧设计的失效内联工具面板
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

## 文章删掉了什么

文章描述的这次修复，是在落地页上做减法，把沉重依赖的加载推到真正用到它们的页面上
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

### 首页上的删除项

文章写明它从首页删掉了那 22 个失效工具面板，指出专门的工具页本来就已经存在，并把 25 KB 的
失效 HTML 记在这次删除上；它还写明它从首页移除了全部 33 个沉重的 `<script>` 标签。它对新的
加载规则的描述是，每个工具页仍然只加载它自己需要的那些库，而文章把这件事标注为按页选择性
加载
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

### 保留下来的那个文件

有一个脚本在这一轮里活了下来。文章写明它保留了一个文件，即 15 KB 的 `common.js`，用于导航、
主题切换以及筛选。它为这个页面打印的结果是 54 KB 的 HTML 加 15 KB 的 JavaScript，它把这个
结果给成总计大约 70 KB，而原先是 4.8 MB
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

## Service Worker 的改动

文章报告了 Service Worker 这一侧的第二处改动
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

### 从库预缓存转向应用外壳预缓存

文章写明它还发现 Service Worker 在安装时预缓存了大约 2 MB 的库，写明它把预缓存收窄到只有
应用外壳、大约 120 KB，并写明沉重的库现在改为在其工具页被首次打开时按需缓存。它补充说
HTML 使用网络优先策略，这样更新可以不经重新安装就到达已安装的应用
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

MDN 记录了上面这几步所用到的缓存机制。关于预缓存，MDN 写明当一个 Service Worker 被安装时，
浏览器会在该 Service Worker 的全局作用域里触发一个 `install` 事件，并写明在这一刻 Service
Worker 可以预缓存资源，把它们从网络取回并存进缓存。MDN 还写明，对于 PWA 确定会需要、在这个
版本的应用中不会改变、并且需要被尽可能快地取到的资源来说，预缓存是一种合适的策略；它给出的
例子是应用的基础用户界面，并补充说如果这部分被预缓存了，那么应用的 UI 就可以在启动时渲染
出来，不需要任何网络请求
([MDN，Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching))。

关于在某个资源被首次请求时缓存它，MDN 写明当一个 Service Worker 的 `fetch` 事件触发时，该
Service Worker 可以把请求转发给网络并缓存得到的响应，要么是因为缓存中原本并不包含一个响应，
要么是为了用一个更新的响应去更新已缓存的那个。关于文章为 HTML 点名的那个策略，MDN 把“网络
优先”描述为：先尝试从网络取回资源，如果请求成功就返回该响应并更新缓存，如果失败就尝试缓存；
并写明这对那些重要的是拿到尽可能新鲜的响应、但一个已缓存资源也好过什么都没有的请求很有用
([MDN，Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching))。

### 给缓存加版本号

文章把它现在遵循的规则写成：每次部署都提升 Service Worker 的缓存版本；并给出了它要防的那种
失败：已安装的应用永远提供陈旧的文件
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

MDN 用它自己的措辞写明了同一条实践。它关于 `Cache` 接口的页面说，`Cache` 里的条目除非被显式
要求否则不会被更新，除非被删除否则不会过期；你要负责实现你的脚本如何处理 `Cache` 更新；并且
你应当确保按名字给缓存加版本，并只从能够安全操作这些缓存的那个版本的脚本里使用它们。它还
写明你要负责定期清理缓存条目
([MDN，Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache))。

MDN 的缓存指南点明了那次清理该放在哪里。它写明一个 PWA 应当在 Service Worker 的 `activate`
事件里清理掉自己缓存的任何旧版本，因为当这个事件触发时，Service Worker 可以确定没有任何先前
版本的 Service Worker 还在运行，所以旧的缓存数据不再需要了
([MDN，Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching))。

## 这篇文章报告的数字

文章给出三个结果。它报告了 422 ms 的页面加载，并把这个数字归因于在真实设备上的 Cloudflare
Web Analytics。它报告 Core Web Vitals 在 LCP、INP 与 CLS 上全部为 100% Good。它还写明这个
PWA 在 4G 上即刻打开。它的收尾建议是去审计一个落地页实际需要什么，而它对自己移除掉的那种
模式的定调是：“以防万一”的脚本标签是移动端性能的隐形杀手
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

第二个结果里的那三个指标名都有已发布的目标值。web.dev 写明 Largest Contentful Paint 衡量
加载性能，并写明为了提供良好的用户体验，LCP 应当在页面首次开始加载之后的 2.5 秒内发生；写明
Interaction to Next Paint 衡量交互性，页面的 INP 应当为 200 毫秒或更少；并写明 Cumulative
Layout Shift 衡量视觉稳定性，页面的 CLS 应当保持在 0.1 或更少。它补充说，一个好的衡量阈值
是页面加载的第 75 百分位，并在移动端与桌面端设备之间分开统计；还写明评估 Core Web Vitals
合规性的工具，应当在三个指标全部于第 75 百分位达到推荐目标时，认为该页面通过
([web.dev，Web Vitals](https://web.dev/articles/vitals))。

## 对 PWA 开发者为什么重要

文章里可迁移的那套机制是安装时的取回。MDN 把 Service Worker 的 `install` 处理函数描述为一次
从网络取回资源并把它们存进缓存的机会；这一步发生在 Service Worker 安装的时候，而不是页面导航
的时候
([MDN，Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching))。

MDN 也写明了什么东西属于那份预缓存的判据。它点名的是 PWA 确定会需要、在这个版本的应用中不会
改变、并且需要被尽可能快地取到的资源，并把应用的基础用户界面作为它的例子
([MDN，Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching))。

文章采纳的那条缓存版本规则有文档可依。MDN 写明 `Cache` 里的条目除非被显式要求否则不会被更新，
除非被删除否则不会过期；并写明缓存应当按名字加版本，并且只从能够安全操作它们的那个版本的脚本里
使用；还在同一处写明一个 `Cache` 存活多久取决于浏览器，并且浏览器可能删除某个源的全部
Cache Storage
([MDN，Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache))。

最后，web.dev 对 Core Web Vitals 的框定是每个指标的第 75 百分位阈值，并在移动端与桌面端之间
分开统计
([web.dev，Web Vitals](https://web.dev/articles/vitals))，这并不是文章为它自己的加载数字所
写明的那套方法
([该文章](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673))。

## 站内相关条目

- [性能](/zh/reference/performance/)：文章的主题是一个已安装应用的启动路径，以及它用来对标的
  那些 Core Web Vitals 指标，而这一页正是 OpenPWA 记录这些指标以及围绕它们的预算的地方。
- [存储](/zh/reference/storage/)：文章在 Service Worker 这一侧所做的改动，关乎缓存里装着
  什么、以及它何时被重新编键，而这一页正是覆盖以源为作用域的那些存储、它们的配额以及它们的
  逐出规则的地方。
- [能力](/zh/reference/capabilities/)：文章从落地页上移走的那些库，存在的理由是从一个工具页
  去驱动平台特性，而这一页正是 OpenPWA 追踪 PWA 能够触达其中哪些特性的地方。

## 尚未验证与存疑之处

- 那个 422 ms 的数字是从哪些浏览器与哪些版本上采集的，文章并未说明，因此这个计时无法钉到
  某个引擎上。
- Core Web Vitals 的结果是如何采集的，以及它是否反映 web.dev 所描述的第 75 百分位阈值，
  文章并未说明。
- 被报告的那个页面加载覆盖哪些页面，以及它是只有落地页还是整个站点，文章的措辞没有给出结论。
- 那个 4.8 MB 的数字是通过网络测得的，还是由文件体积相加得来的，并未说明，因此文章打印的
  98.5% 这个数字无法追溯到一种方法。
- 文章描述的那个应用外壳预缓存是否在 `activate` 事件里被清理，也就是 MDN 点名的那个该做这件
  事的地方，并未被涉及。
- 那些沉重库的按需缓存随时间推移会给该源的存储占用带来什么，文章没有讨论；MDN 写明一个 `Cache`
  存活多久取决于浏览器，并且浏览器可能删除某个源的全部 Cache Storage
  ([MDN，Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache))。
- 文章为 HTML 采纳的那个网络优先策略在这个应用里是否有一个有文档可依的离线回退，并未说明。

## 参考来源

- [How I Cut My Homepage from 4.8MB to 70KB (and Made My PWA Open Instantly)](https://dev.to/shahzaib11/how-i-cut-my-homepage-from-48mb-to-70kb-and-made-my-pwa-open-instantly-3673)（dev.to）
- [Caching](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching)（developer.mozilla.org）
- [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache)（developer.mozilla.org）
- [Web Vitals](https://web.dev/articles/vitals)（web.dev）