发布于 2026-01-06 9 阅读
0

提升网站性能的一个绝妙技巧(其实不然)

提升网站性能的一个绝妙技巧(其实不然)

我经历过的最划算的性能提升案例就是删除了两行 JavaScript 代码。

我的背景

我在亚马逊工作时,隶属于卖家中心部门,负责开发帮助商家销售产品的工具。我主要负责的应用程序是一个复杂的多部分表单,它被拆分成多个标签页,包含数十个输入框,这些输入框会根据产品类型、客户特征以及用户在填写过程中做出的各种选择动态填充。该应用程序使用 React 和 Redux 构建,后端则是一个基于 SpringMVC 的自定义 Java 框架。

问题

作为一家公司,亚马逊非常重视网页性能,但同时也重视代码的快速交付。这两种相互冲突的需求导致了摩擦;眼睁睁看着一个月来为提升页面性能所做的努力,却因为新功能带来的意想不到的负面影响而付诸东流,这无疑令人沮丧。我刚加入公司时,是团队中唯一的前端工程师,也是整个组织中为数不多的前端工程师之一,我的主要工作是前端架构和网页性能优化。我的职责是找到可持续的方法来达成这些目标,同时又不影响代码的交付速度。当时,我们经常无法达到网页性能目标。团队中的大多数成员都是优秀的后端开发人员,但很少有人拥有 React 或前端性能优化方面的经验。

失败的尝试

和许多新员工一样,我刚来的时候也想当救世主,力挽狂澜。我首先寻找那些容易实现、收益高的性能优化点:我们是否使用了针对 webpack 优化的 lodash 构建?我们是否进行了打包拆分?fetch我们的打包文件中到底有多少 polyfill?我之前做过 React 应用的性能优化,脑子里已经列好了清单。然而,问题在于这些唾手可得的优化并没有带来足够的实际收益。我们这里精简了 10kb,那里精简了 100kb。我们的打包大小从 1.8mb 降到了 1.5mb,最终甚至降到了 1mb 多一点,但我们仍然无法达到性能目标。我们大量依赖真实用户监控来了解用户体验。最终我们发现,由于用户与我们应用的交互方式,我们的缓存命中率相当高。减小JS包的大小固然是好事,但用户体验的性能提升远未达到我们的预期。肯定还有其他方法可以提高速度。

突破

突破往往发生在我完成所有检查清单并开始探索不熟悉的领域之后,就像有时那样。我一直在寻找新的方法来分析我们应用程序中哪些功能有效,哪些无效,就在那时,我偶然发现了 Chrome 开发者工具中的“代码覆盖率”选项卡。找到它的过程相当繁琐;它隐藏在 Chrome 开发者工具三点菜单“更多工具”下的两层菜单里,或者你也可以通过在开发者工具中输入命令来激活命令菜单⌘P>然后输入命令查看其他可用操作,最后输入代码覆盖率来找到它coverage。第一次看到它的结果时,我简直如获至宝,兴奋得在推特上分享了这件事。

“覆盖率”选项卡可以显示页面上未使用的 JS 和 CSS 文件。进入覆盖率面板后,默认情况下会同时显示 JS 和 CSS 文件。但您也可以筛选出仅显示 CSS 文件。

显示覆盖范围 选择 CSS

我发现我们主 CSS 文件中超过 98% 的代码都没有被使用。我还意识到,这个 CSS 文件本身就超过 1MB。我一直在努力精简 JS 包,使其尽可能小,但 CSS 文件的影响却更大!下面的 CSS 代码使用情况数据来自另一个网站,但情况类似

CSS覆盖率非常低

大型 CSS 文件的问题

虽然讨论大型 JS 文件包的弊端很常见,但大型 CSS 文件包的危害可能更大!CSS 是一种渲染阻塞资源,这意味着浏览器必须等待 CSS 文件下载、解析并构建成CSSOM 树后才能渲染页面内容。如今,JS 文件通常会添加到 `<head>` 标签的末尾或通过 `<script> ` 或 `<script>`标签<body>引入,而 CSS 文件很少与页面渲染并行加载。因此,务必将未使用的 CSS 文件从主 CSS 文件包中分离出来。deferasync

多年来,人们一直在讨论只在页面初始加载时加载“首屏”或关键路径上的 CSS,但尽管有一些工具可以尝试自动化这个过程,它仍然并非万无一失。说到避免加载不必要的 CSS,我想很多人都会同意,相比于那种将网站所有样式都放在一个大型 Sass 或 LESS 文件中的常见做法,CSS-in-JS 方法甚至 CSS Modules 都做得更好。

找出问题所在

我们团队最初的样式设计方法是使用一个大型的 Sass 文件,其中包含几十个依赖的样式表,并通过 `@import` 语句导入。这使得我们很难确定哪些部分正在使用,哪些部分没有使用。我花了几个小时仔细检查我们的 CSS 文件,寻找未使用的样式。看起来并没有什么明显的浪费,我当然也找不到一兆字节的额外未使用样式。这些 CSS 会来自哪里呢?是不是来自包含额外样式的共享页眉/页脚?或者是在某个地方通过 JavaScript 导入的 CSS?我必须找到答案。

在检查我们的 JS 代码后,我只找到了 4 到 5 个 CSS 导入语句。我们的 webpack 配置确保所有从 JS 文件内部导入的 CSS 最终都会打包到一个大文件中。在我们的主 JavaScript 入口文件 (index.js) 中,我发现了 2 个看起来特别可疑的 CSS 导入语句。以下代码并非完全相同,但内容非常相似:

import 'semantic-ui/dist/styles.min.css'
import 'semantic-ui/dist/styles.css'
Enter fullscreen mode Exit fullscreen mode

我之前看过这段代码不下几十次,但都置之不理。不过,这次我面临一个新挑战:找出多余的 CSS 代码来自哪里。这段代码就显得格外引人注目。我们为什么要导入这个库?我们真的需要它吗?而且,为什么我们要导入两次(一次是压缩版,一次是未压缩版)?

我做的第一件事就是把这两行代码都注释掉了。npm run build然后我运行了一下,发现我们的 CSS 包大小从 1.25MB 降到了 30KB!简直太离谱了。这段代码简直要了我们的命。☠️

不出所料,移除 CSS 后我们的网站看起来糟透了。我们之前依赖于这些 CSS 包中的某些内容。接下来,我逐一注释掉了它们。奇怪的是,我们需要保留未压缩的 CSS 包,以免破坏网站的视觉效果,但至少我取得了进展。仅仅删除一行代码,我们就节省了大约 500kb 的 CSS 文件。

现在,更困难的部分开始了,那就是彻底摆脱我们对那个 UI 库的依赖。

剩下的

和许多团队一样,我们依赖于一个内部 UI 库,我们的应用程序已经导入了这个库。我想,我们或许可以用这个内部库来提供大部分(如果不是全部)我们从外部库获得的功能。

我最初采用的方法是直接将整个 Semantic UI 库的 CSS 代码复制粘贴到一个新文件中,然后删除不需要的部分。这种方法虽然取得了一些成效,但随着样式层级越来越厚、越来越复杂,操作也变得越来越困难。最终,我彻底移除了 CSS 导入,故意破坏了网站的显示效果。这样一来,我们就能轻松地识别出实际使用的类。我们截取了正常网站的屏幕截图,然后仔细地与出错的版本进行了比较。

事实证明,我们主要使用了三个组件:

  • 网格系统
  • 导航标签
  • 模态对话框

一旦我们确定了所使用的库组件,就很容易在代码库中搜索并找出哪些组件依赖于它们。例如,很多组件都使用了网格布局,但对于那些只需要修改少量类名的组件,我们找到了一个现成的替代方案。在其他一些情况下,我们需要添加新的 CSS 或稍微调整一下 HTML 代码,才能使其与我们的其他 UI 库兼容。最终,一位新团队成员花了大约一个月的时间,才将我们完全从该外部库中移除。我们仔细审查了她的工作,对比了修改前后的截图,并在发现细微的样式差异时,请几位团队成员审核,以确保修改后的版本与原版本足够接近,不会影响后续的开发。

影响

更新发布后,我们查看了真实用户监控图表,发现应用交互时间(TTI)的第 50 和第 90 百分位数均大幅缩短。其中,第 90 百分位数的 TTI 缩短了约半秒。之前做了很多改动,但似乎都没什么效果,如今终于看到性能有了显著提升,真是令人欣慰。

移除那个 UI 库包最终产生的影响,可能比我在亚马逊从事 Web 性能相关工作期间所见过的任何其他单一更改都要大。

要点总结

我发现很难概括所有提升网页性能的方法。你的应用重复导入大型 CSS 库的可能性有多大?你可以检查一下,但这种情况发生的概率很低。我希望你能从我的经验中了解到,我们是如何发现并解决这个问题的。

不要仅仅按照清单进行优化(要学会使用工具!)

比较容易的部分在于流程:你不能仅仅按照清单进行优化。在进行性能优化时,清单固然重要,因为许多应用程序可以通过一系列简单易行、广为人知的改进措施来提升性能。你可以也应该利用你过去以及社区在性能优化方面所做的工作。但是,当你完成清单上的所有步骤后,你需要培养持续深入挖掘的能力。仅仅因为你之前参与过的其他应用程序受益于 A 或 B 更改,并不意味着它在你的下一个应用程序中也适用。你必须了解你的工具。你必须了解你网站的具体特性和架构。而且你必须了解你的客户。Lighthouse 可能在这个过程的早期就告诉我,我的页面上的 CSS 代码太多了。由于我对 CSS 文件的构建方式缺乏清晰的理解,也没有更好的分析工具,我无法有效地利用这些信息。虽然常见网页性能错误清单肯定很有帮助,但教团队成员如何使用现有工具来具体分析网页性能则更加有效。

制定强有力的网站性能要求

不过,另一个重要的收获是关于文化。要构建高性能应用程序,性能本身必须成为首要的关键绩效指标 (KPI)。我认为很多工程师都乐于优化,这的确是一项充满乐趣又极具挑战性的工作。但我们都知道,优化结果可能非常不稳定。我数不清有多少次承诺将用户体验缩短 150 毫秒,在本地测试时确实实现了这一改进,但实际上线后却没有任何效果,甚至出现了负面影响。在很多情况下,这会让工程或产品经理对这类承诺心存疑虑。我在亚马逊的团队在 Web 性能方面拥有卓越的领导力。正是这种领导力确保了我们能够获得所需的支持,从而坚持不懈地努力,直到达到我们想要的效果。


我并不指望这篇文章能为那些试图优化应用程序的人提供任何灵丹妙药,但我希望它能鼓励你们继续深入研究,直到找到自己的方法。


PS:我要特别感谢我的同事Scott GiffordMichael Kirlin。Scott一直是亚马逊网络性能领域极具影响力的工程师,在我任职期间一直指导着我。Michael 不仅审阅了这篇文章,还对其进行了大量的编辑,使其更加清晰易懂。谢谢各位!

文章来源:https://dev.to/xjamundx/one-cool-trick-to-speed-up-your-website-performance-not-really-1219