[译] 2019 前端性能优化年度总结 — 第一部分

25,151 阅读19分钟

让 2019 来得更迅速吧~你正在阅读的是 2019 年前端性能优化年度总结,始于 2016。

当我们在讨论前端性能时我们在谈些什么?性能的瓶颈又到底在哪儿?是昂贵的 JavaScript 开销,耗时的网络字体下载,超大的图片还是迟钝的页面渲染?摇树(tree-shaking)、作用域提升(scope hoisting)、代码分割(code-splitting),以及各种酷炫的加载模式,包括交叉观察者模式(intersection observer)、服务端推送(server push)、客户端提示(clients hints)、HTTP/2、service worker 以及 edge worker,研究这些真的有用吗?还有,最重要的,当我们着手处理前端性能的时候,我们该从哪里开始,该如何去建立一个长期的性能优化体系?

早些时候,性能都是所谓的“后顾之忧”。直到项目快结束的时候,它会被归结为代码压缩(minification)、拼接(concatenation)、静态资源优化(asset optimization)以及几行服务器配置的调整。现在回想一下,情况似乎已经全然不同了。

性能问题不仅仅是技术上的考量,当它被整合进工作流时,在设计的决策中也需要考量性能的因素。性能需要持续地被检测、监控和优化。同时,网络在变得越来越复杂,这带来了新的挑战,简单的指标追踪变得不再可行,因为不同的设备、浏览器、协议、网络类型和延迟都会使指标发生明显变化。(CDN、ISP、缓存、代理、防火墙、负载均衡和服务器,这些都得考虑进去。)

因此,如果我们想囊括关于性能提升的所有要点 — 从一开始到网站最后发布,那么最终这个清单应该长啥样呢?以下是一份(但愿是无偏见的、客观的)2019 前端性能优化年度总结,“介是你没有看过的船新版本”,它几乎包括所有你需要考虑的要点,来确保你的网站响应时间够短、用户体验够流畅、同时不会榨干用户的带宽。

目录

起步:计划与指标

对于持续跟踪性能,“微优化”(micro-optimization)是个不错的主意,但是在脑子里有个明晰的目标也是很必要的 — 量化的目标会影响过程中采取的所有决策。有许多不同的模型可以参考,以下讨论的都基于我个人主观偏好,请根据个人情况自行调整。

1. 建立性能评估规范

在很多组织里面,前端开发者都确切地知道哪有最有可能出现问题,以及应该使用何种模式来修正这些问题。然而,由于性能评估文化的缺失,每个决定都会成为部门间的战场,使组织分裂成孤岛。要想获得业务利益相关者的支持,你需要通过具体案例来说明:页面速度会如何影响业务指标和他们所关心的 KPI

没有开发、设计与业务、市场团队的通力合作,性能优化是走不远的。研究用户抱怨的常见问题,再看看如何通过性能优化来缓解这些问题。

同时在移动和桌面设备上运行性能基准测试,由公司真实数据得到定制化的案例研究(case study)。除此以外,你还可以参考 WPO Stats 上展示的性能优化案例研究及其实验数据来提升自己对性能优化的敏感性,了解为什么性能表现如此重要,它对用户体验和业务指标会产生哪些影响。光是明白性能表现很重要还不够,你还得设立量化的、可追溯的目标,时刻关注它们。

那么到底该怎么做呢?在 Allison McKnight 名为 Building Performance for the Long Term 的演讲中,她详细地分享了自己如何在 Etsy 建立性能评估文化的案例

Brad Frost and Jonathan Fielding’s Performance Budget Calculator

Brad Frost 的 Performance budget builder 和 Jonathan Fielding 的 Performance Budget Calculator 可以帮助你建立性能预算并将其可视化表示出来。(预览

2. 目标:比你最快的竞争对手快至少 20%

根据一项心理学研究,如果你希望你的用户感觉到你们的网站用起来比竞争对手快,那么你需要比他们快至少 20%。研究你的主要对手,收集他们的网站在移动和桌面设备上的性能指标,确定超越他们的最低要求。为了得到准确的结果和目标,首先去研究你们产品的用户行为,之后模仿 90% 用户的行为来进行测试。

为了更好地了解你的对手的性能表现,你可以使用 Chrome UX ReportCrUX,一组现成的 RUM 数据集,Ilya Grigorik 的视频介绍),Speed Scorecard(可同时估算性能优化将如何影响收入),真实用户体验测试比较(Real User Experience Test Comparison)或者 SiteSpeed CI(基于集成测试)。

注意:如果你使用 Page Speed Insights(是的,它还没被抛弃),你可以得到指定页面详细的 CrUX 性能数据,而不是只有一些粗略的综合数据。在为具体页面(如“首页”、“产品列表页面”)设立性能目标时,这些数据会非常有用。另外,如果你正在使用 CI 来监测性能预算,当使用 CrUX 来确立目标时,你需要确保测试环境与 CrUX 一致。(感谢 Patrick Meenan!)

收集数据,建立一个表格,削减掉 20%,以此建立你的目标性能预算。那么现在你有了量化的对照组样本。事情正逐步走向正轨,只要你时刻把这份预算记在心里,并且每次都交付尽可能少的代码以缩短可交互时间。

需要些资料来上手?

一旦确立好合适的性能预算,你就可以借助 Webpack Performance Hints and BundlesizeLightouse CI, PWMetricsSitespeed CI 把它们整合进打包流程中,在请求合并时强制检测性能预算,并在 PR 备注中注明得分记录。如果你需要个性化定制,你可以使用 webpagetest-charts-api,它提供了一系列可以从 WebPagetest 的结果生成图表的 API。

举个例子,正如 Pinterest 一样,你可以创建一个自定义的 eslint 规则,禁止导入重依赖(dependency-heavy)的文件和目录,从而避免打包文件变得臃肿。设定一个团队内共享的“安全”依赖包列表。

除了性能预算外,仔细考虑那些对你们业务价值最大的关键用户操作。规定并讨论可接受的关键操作响应时间阈值,并就“UX 就绪”耗时评分在团队内达成共识。大多数情况下,用户的操作流程会涉及到许多不同公司部门的工作,因此,就“时间阈值”达成共识可以为今后关于性能的沟通提供支持,避免不必要的讨论。确保对新增资源和功能带来的资源开销了如指掌。

另外,正如 Patrick Meenan 提议的,在设计过程中,规划好加载的顺序和取舍是绝对值得的。如果你预先规划好哪部分更重要,并确定每部分出现的顺序,那么同时你也会知道哪些部分可以延迟加载。理想情况下,这个顺序也会反映出 CSS 和 JavaScript 文件的导入顺序,因此在打包阶段处理它们会变得更容易些。除此以外,还得考虑页面加载时中间态的视觉效果(比方说,当网络字体还没有加载完全时)。

规划,规划,规划。尽管在早期就投入那些能起到立竿见影效果的优化似乎相当有吸引力 — 这对需要快速决胜的项目而言可能是个不错的策略,但是如果没有务实的规划和因地制宜的性能指标,很难保证性能优先能一直受到重视。

首次绘制(First Paint)、首次有内容绘制(First Contentful Paint)、首次有意义绘制(First Meaningful Paint)、视觉完备(Visual Complete)、首次可交互时间(Time To Interactive)的区别。完整文档。版权:@denar90

3. 选择合适的指标

并不是所有的指标都同等重要。研究哪个指标对你的应用最重要,通常来说它应该与开始渲染你的产品中最重要的那些像素的速度以及提供输入响应所需的时间相关。这个要点将为你指明最佳的优化目标,提供努力的方向。

不管怎样,不要总是盯着页面完整载入的时间(比方说 onloadDOMContentLoaded),要站在用户的角度去看待页面加载。也就是说,需要关注一组稍微不同的指标。事实上,“选择正确的指标”是没有绝对完美方案的。

根据 Tim Kadlec 的研究和 Marcos Iglesias 在他的演讲中提到的,传统的指标可以归为几种类型。通常,我们需要所有的指标来构建完整的性能画像,但是在特定场景中,某些指标可能比其他的更重要些。

  • 基于数量的指标衡量请求数量、权重和性能评分等。对于告警和监控长期变化很有用,但对理解用户体验帮助不大。

  • 里程碑式指标使用加载过程中的各个状态来标记,比如:首位字节时间(Time To First Byte)首次可交互时间(Time To Interactive)。对于描述用户体验和指标很有用,但对了解加载过程中的情况帮助不大。

  • 渲染指标可以估计内容渲染的时间,例如渲染开始时间(Start Render)速度指数(Speed Index)。对于检测和调整渲染性能很有用,但对检测重要内容何时出现、何时可交互帮助不大。

  • 自定义指标衡量某个特定的、个性化的用户事件,比如 Twitter 的首次发推时间(Time To First Tweet),Pinterest 的 收藏等待时间(PinnerWaitTime)。对准确描述用户体验很有用,但不方便规模化以及与竞品比较。

为了使性能画像更加完整,我们通常会在所有类型中都选择一些有用的指标。一般来说,最重要的是以下几个:

  • 首次有效绘制(First Meaningful Paint,FMP)

    反映主要内容出现在页面上所需的时间,也侧面反映了服务器输出任意数据的速度。FMP 时间过长一般意味着 JavaScript 阻塞了主线程,也有可能是后端/服务器的问题。

  • 首次可交互时间(Time to Interactive,TTI)

    在此时间点,页面布局已经稳定,主要的网络字体已经可见,主线程已可以响应用户输入 — 基本上意味着只是用户可以与 UI 进行交互。是描述“网站可正常使用前,用户所需要等待的时长”的关键因素。

  • 首次输入延迟(First Input Delay,FID 或 Input responsiveness)

    从用户首次与页面交互,到网站能够响应该交互的时间。与 TTI 相辅相成,补全了画像中缺少的一块:在用户切实与网站交互后发生了什么。标准的 RUM 指标。有一个 JavaScript 库 可以在浏览器中测量 FID 耗时。

  • 速度指数(Speed Index)

    衡量视觉上页面被内容充满的速度,数值越低越好。速度指数由视觉上的加载速度计算而得,只是一个计算值。同时对视口尺寸也很敏感,因此你需要根据目标用户设定测试配置的范围。(感谢 Boris!)

  • CPU 耗时

    描述主线程处理有效负载时繁忙程度的指标,显示在绘制、渲染、运行脚本和加载时,主线程被阻塞的频次和时长。高的 CPU 耗时明显地意味着卡顿的用户体验。利用 WebPageTest,你可以在 “Chrome” 标签页上选择 “Capture Dev Tools Timeline” 选项来暴露出可能的主线程崩溃(得益于 WebPageTest 可以在任何设备上运行)。

  • 广告的影响(Ad Weight Impact)

    如果你的站点的利润主要来源于广告,那么追踪广告相关代码的体积就很有用了。Paddy Ganti 的脚本可以构筑两条 URL(一条有广告,一条没有),并且利用 WebPageTest 生成一个比较视频,并显示区别。

  • 偏离度指标(Deviation metrics)

    正如 Wikipedia 的工程师所指出的,你的结果中数据的变化在一定程度上可以反映出设施的可靠性,以及你该花多少精力来关注这些偏离度和极端值。过大的变化意味着你很可能需要对目前设施的配置做一些调整,它也能帮助我们了解有某些页面是难以可靠地用指标衡量的,例如因为第三方脚本而导致的明显变化。另外,追踪浏览器版本也是个不错的主意,它可能帮助你获悉新版浏览器可以带来的性能变化。

  • 自定义指标(Custom metrics)

    自定义指标可由具体业务和用户体验的需要专门设置。它需要你对重要像素、关键脚本、必要 CSS 样式和相关静态资源有个清晰的概念,并能够测算用户需要多长时间来下载它们。关于这点,你可以使用 Hero Rendering TimesPerformance API,为重要业务事件创建时间戳。另外,你也可以通过在 WebPageTest 测试完成后运行自定义的脚本来收集自定义的指标

Steve Souders 写了一篇文章详细地介绍了各个指标。需要注意的是:首次交互时间是在实验环境下通过自动化审查得到的,而首次输入延迟则表示真实用户在使用中感受到的实际延迟。总而言之,始终观测和追踪这两个指标会是个好主意。

不同的应用,偏好的指标可能会不同。举个例子,对于 Netflix TV 的 UI 界面而言,关键输入响应、内存使用和首次可交互时间会更重要些,而对于 Wikipedia,首末视觉变化和 CPU 耗时指标会显得更重要些。

注意:FID 和 TTI 都不关心滚动表现。滚动事件可以独立发生,因为它是主线程外的。因此,对于许多内容为主的站点而言,这些指标可能并不是很重要。(感谢 Patrick!)。

以用户为中心的性能指标可以帮助更好地了解真实用户体验。首次输入延迟(FID)是一个尝试去实现这一目标的新指标。(戳此了解详情

4. 在目标用户的典型设备上收集数据

为了得到准确的数据,我们需要选择合适的测试设备。Moto G4 会是一个不错的选择,或者是 Samsung 的一款中端产品,又或者是一款如 Nexus 5X 一样中庸的设备,以及 Alcatel 1X 这样的低端设备。你可以在 open device lab 找到这些。如果想在更慢的设备上测试,你可以花差不多 $100 买一台 Nexus 2。

如果你手上没有合适的设备,你可以通过网络限速(比如:150ms RTT,下行 1.5Mbps,上行 0.7Mbps)以及 CPU 限速(慢 5 倍)在电脑上模拟移动端体验。然后,再切换到普通 3G、4G 和 WIFI 网络进行测试。为了使性能影响更加明显,你甚至可以引入 2G 星期二,或者为了更方便测试,在办公室限制 3G 网络

时刻记着:在移动设备上,运行速度应该会比在桌面设备上慢 4-5 倍。移动设备具有不同的 GPU、CPU、内存、电池特性。如果说慢速网络制约了下载时间的话,那么手机较为慢速的 CPU 则制约了解析时间。事实上,移动设备上的解析时间通常要比桌面设备长 36%。因此,一定要在一部平均水准的设备上进行测试 — 一部你的用户中最具代表性的设备。

Introducing the slowest day of the week

在一周中选择一天让网速变慢。Facebook 就有 2G 星期二来提高对低速网络的关注。(图片来源

幸运的是,有很多工具可以帮你自动化完成数据收集、评估上述性能指标随时间变化趋势。记住,一个好的性能画像应该包括一套完整的性能指标、实验数据和实际数据

  • 集成测试工具可以在预先规定了设备和网络配置的可复制环境中收集实验数据。例如:LighthouseWebPageTest
  • 真实用户监测(RUM) 工具可以持续评估用户交互,收集实际数据。例如,SpeedCurveNew Relic,两者也都提供集成测试工具。

前者在开发阶段会非常有用,它可以帮助你在开发过程中发现、隔离、修复性能问题。后者在维护阶段会很有用,它可以帮助你了解性能瓶颈在哪儿,因为这都是真实用户产生的数据。

通过深入了解浏览器内置的 RUM API,如 Navigation TimingResource TimingPaint TimingLong Tasks 等,集成测试和 RUM 两者搭配构建出完整的性能画像。你可以使用 PWMetricsCalibre, SpeedCurvemPulseBoomerangSitespeed.io 来进行性能监测,它们都是不错的选择。另外,利用 Server Timing header,你甚至可以同时监测后端和前端性能。

注意: 建议使用浏览器外部的网络节流器,因为浏览器的 DevTools 可能会存在一些问题,比如:由于实现方法的原因,HTTP/2 push 可能会有问题。(感谢 Yoav 和 Patrick!)对于 Mac OS,我们可以用 Network Link Conditioner;对于 Windows,可以用 Windows Traffic Shaper;对于 Linux,可以用 netem;对于 FreeBSD,可以用dummynet

Lighthouse

Lighthouse — DevTools 自带的性能审查工具。

5. 为测试设立“纯净”、“接近真实用户”的浏览器配置(Profile)

使用被动监控工具进行测试时,一个常见的做法是:关闭反病毒软件和 CPU 后台任务,关闭后台网络连接,使用没有安装任何插件的“干净的”浏览器配置,以避免结果失真。(FirefoxChrome)。

然而,了解你的用户通常会使用哪些插件也是个不错的主意,然后使用精心设计的“接近真实用户的”浏览器配置进行测试。事实上,某些插件可能会给你的应用带来显著的性能影响。如果你有很多用户在使用这些插件,你可能需要考虑这些影响。“干净的”用户浏览器配置可能有些过于理想化了,可能会与实际情况大相径庭。

6. 与团队其他成员分享这份清单

确保你的每一位同事都充分熟悉这份清单,从而避免在以后出现误解。每一个决策都会带来性能影响,整个项目会从前端开发者正确地对待性能问题而获益良多,从而使得团队中的每一个人都负起责任来,而不仅仅只是前端。根据性能预算和清单中定义的优先级来制定设计决策。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏