今天看啥  ›  专栏  ›  三木的旅行日记

阅读笔记之-《前端性能检查表(Front-End Performance Checklist)》

三木的旅行日记  · 掘金  · 前端  · 2020-02-12 03:35
阅读 12

阅读笔记之-《前端性能检查表(Front-End Performance Checklist)》

Vitaly Friedman每年都会发一份新的《前端性能检查表(Front-End Performance Checklist)》,对前一年的内容进行补充修改,2018年和2019年的内容大家可以在掘金上找到翻译版本。
这份前端性能检查表内容非常多而且包含了很多学习的扩展链接。作者主要是列出一份清单,让大家作为性能优化大纲。里面有很多超出大家平时工作范围的东西,但也不必在意,忽略掉就好了。
在看这份list的时候做了一下简单的笔记,给大家划一下重点。了解每一部分讲什么,在根据自己的需要去看详细内容就方便很多了。
今年的2020版一月份就已经发布了,但还没有人去翻译,对于英文阅读没有障碍的同学可以去看一下,变化并不大,主要是有些点提供了一些新的阅读和学习资源。

第一部分:起步:计划与指标

建立性能评估规范

要想在项目中进行性能优化,单靠前端开发是不可能持续进行的,所以你要建立一套有效的评估规范。了解产品的在各个情况下的,另外还要设立量化的、可追溯的目标,时刻关注它们。 进而展示给大家,页面速度会如何影响业务指标和他们所关心的 KPI,让他们成为你的业务利益相关者。

目标:比你最快的竞争对手快至少 20%
  • 了解你的对手的性能表现
  • 收集数据,建立一个表格,削减掉 20%,以此建立你的目标性能预算
  • 仔细考虑那些对你们业务价值最大的关键用户操作。规定并讨论可接受的关键操作响应时间阈值,
  • 规划好加载的顺序和取舍,务实的规划和因地制宜的性能指标,可以保证性能优先能一直受到重视。
选择合适的指标

根据你的系统性质,合理的取舍你认为重要的指标。

  • 基于数量的指标衡量请求数量、权重和性能评分等
  • 里程碑式指标使用加载过程中的各个状态来标记
  • 渲染指标可以估计内容渲染的时间
  • 自定义指标衡量某个特定的、个性化的用户事件
在目标用户的典型设备上收集数据

作为开发人员总是拥有最好的电脑,最快的网速,但这些都是大多数用户所不具备的,所以我们测试的结果并不能代表用户实际效果, 所以除了很多自动化测试的工具,对于前端开发人员来说,最方便的就是掌握浏览器内置的 RUM API,可以帮助我们构建出完整的性能画像。

第二部分:定制切实可行的目标

100 毫秒响应时间,60 fps
  • 为了使用户感觉交互流畅,界面的响应时间不得超过 100ms。
  • 每一帧动画应在 16 毫秒内完成,从而达到每秒 60 帧(1 秒 ÷ 60 = 16.6 毫秒)—— 最好在 10 毫秒以下。

这两个目标基本上每个前端工程师都知道,它们是适用于运行时性能,但在开发中很少有人去真正度量优化这个两个指标。

速度指数 < 1250,TTI(交互时间) < 5s(3G),关键文件大小 < 170KB(gzip 压缩后)

由于

  • TCP 慢启动,
  • 内存和 CPU 有 硬件限制影响 JavaScript 的解析时间。

导致我们很难达成这个目标,为了实现所述目标,我们必须考虑 JavaScript 关键文件大小。Google 的 Alex Russels 建议将 gzip 压缩后大小为 130-170KB 作为一个合理的上限,当超出这个预算时,你应该进行慎重考虑。

第三部分:定义环境

选择并设置你的构建工具

坚持你自己的构建环境,无论是 Grunt、Gulp、Webpack、Parcel 还是工具组合。只要你获得了所需结果,并且构建过程中没有任何问题,这就可以了。

默认使用渐进增强

保持 渐进增强 作为前端架构和部署的指导原则是一个安全的选择。首先设计和构建核心体验,然后使用高级特性为支持的浏览器提升体验,创建 弹性 体验。

选择一个高性能基准

由于我们的系统运行在客户端,所以有很多未知因素影响加载速度(网络,热量限制,第三方脚本,缓存替换,解析器阻塞模式,磁盘 I/O,IPC 延迟,已安装的扩展,杀毒软件和防火墙等)。 我们必须彻底审核我们选择不同框架的 网络传输成本,解析/编译时间和运行时成本。

评估每个框架以及它们的依赖项
  • 是否有必要使用框架:有些简单项目使用框架反而引入很多不必要的依赖和配置,增大了代码体积
  • 慎重选择框架:通过探索功能、可访问性、稳定性、性能、包生态系统、社区、学习曲线、文档、工具、跟踪记录和团队来评估
考虑使用 PRPL 模式以及应用程序 shell 架构

构建 Web 应用程序时,请查看

  • PRPL模式
  • 应用程序 shell 体系结构。

这个想法非常简单:推送初始路由交互所需的最少代码,以便快速渲染,然后使用 service worker 进行缓存和预缓存资源,然后异步地延迟加载所需的路由。

你是否优化了各个 API 的性能?

许多资源需要来自 API 的数据,API 可能会成为性能瓶颈。

  • 当从 API 检索数据,服务器响应中的任何延迟都将传播给最终用户,从而延迟渲染。
  • 通过 RESTful 返回的数据量通常大于渲染该组件所需的数据量。

有条件的话尝试使用GraphQL,它为这些问题提供了高性能的解决方案。(但很多公司后端话语权比较高,改造使用GraphQL成本和阻力都会很大,应该慎重)

明智地选择你的 CDN
  • 将内容的某些部分“外包”到 静态站点生成器,将其推送到 CDN 并从中提供静态版本,从而避免数据库请求。
  • CDN 也可以提供(和卸载)动态内容。

第四部分:资源优化

使用 Brotli 或 Zopfli 来对纯文本进行压缩
  • Brotli:一种新开源的无损数据格式,拥有更高的压缩率,更快的解压速度。
  • Zopfli 的压缩算法:任何普通的 Gzip 压缩资源都可以通过 Zopfli 改进的 Deflate 编码达到比 Zlib 的最大压缩率小 3% 到 8%的文件大小。
使用响应图像和 WebP
  • 尽量使用带有 srcset、sizes 属性的响应式图片和 元素响应式图片。
  • 可以通过在原生 上使用 WebP 图片以及回退到 JPEG 的机制或者使用协议内容的方式中使用 WebP 格式(在 Chrome、Opera、Firefox 65、Edge 18 中都被支持的格式)
图像的优化是否得当?

这部分内容主要设计更加细化的图像优化方法,比如:

  • 对于 PNG 来说,我们可以使用 Pingo,对于 SVG 来说,我们可以使用 SVGO 或 SVGOMG。如果你需要快速预览、复制或下载网站上的所有 SVG 资源,那么你可以尝试使用 svg-grabber。
  • 要记得清理未使用的资源,删除不必要的元数据以及图稿中的路径点数量(比如 SVG 这类代码)

对于图片性能要求比较高的同学可以详细阅读。

视频优化是否得当?

对于视频优化的详细讨论。 比如GIF动画、动态WebP、 HTML5 videos的性能和现状,以及为了解决视频加载性能而不断提出的新视频格式。

Web 字体优化过了么?
  • 否可以首选 UI 系统字体。
  • WOFF2 的支持性是最好的,你可以使用 WOFF 作为不支持 WOFF2 的浏览器的备用选项
  • 更好的选项应该是使用 Critical FOFT 结合 preload 和 "The Compromise" 方法。它们都使用两阶段渲染来逐步提供 Web 字体
  • 使用 preload 资源提示来预加载字体

第五部分:构建优化

确定优先级
  • 了解你首先要处理什么
  • 列出你全部的静态资源清单(JavaScript、图片、字体、第三方脚本以及页面上的大模块:如轮播图、复杂的信息图表和多媒体内容),并将它们分组。
  • 定义旧版浏览器的基本核心体验(即完全可访问的核心内容)、现代浏览器的增强体验(即更加丰富的完整体验)以及额外功能

在优化的时候立即加载核心体验,然后加载增强体验,最后加载额外功能。

重温优秀的“符合最低要求”技术
  • 使用符合最低要求(cutting-the-mustard)技术 将核心体验发送到旧版浏览器,并为现代浏览器提供增强体验。
解析 JavaScript 是耗时的,所以让它体积小
  • 大多数性能问题都来自启动应用程序的初始解析时间。
  • JavaScript 有一个解析的成本,但很少仅是由于文件大小一个因素影响性能。
  • 我们需要找到编写和部署更少量 JavaScript 的方法。
使用了摇树、作用域提升和代码分割吗
  • 摇树(tree-shaking)是一种在 webpack 中清理构建过程的方法,它仅将实际生产环境使用的代码打包,并排除没有使用的导入模块。
  • 你可能需要考虑学习如何编写高效的 CSS 选择器,以及如何避免臃肿且耗时的样式。
  • 代码拆分(code-splitting)是另一个 webpack 功能,它将你的代码库拆分为按需加载的“块”。
  • 考虑使用 preload-webpack-plugin,它接受代码拆分的路由,然后提示浏览器预加载它们。
可以将 JavaScript 切换到 Web Worker 中吗?

为了减少对首次可交互时间(Time-to-Interactive)的负面影响,考虑将高耗时的 JavaScript 放到 Web Worker 或通过 Service Worker 来缓存。 请注意,Web Worker 无权访问 DOM,因为 DOM 不是“线程安全的”,而且它们执行的代码需要包含在单独的文件中。

可以将 JavaScript 切换到 WebAssembly 中吗?

我们还可以将 JavaScript 转换为 WebAssembly,这是一种二进制指令格式,可以使用 C/C++/Rust 等高级语言进行编译。它的浏览器支持非常出色.

是否使用了 AOT 编译?

使用 AOT(ahead-of-time)编译器将一些客户端渲染放到服务器,从而快速输出可用结果。

仅将遗留代码提供给旧版浏览器

为了兼容旧版本的浏览器,我们可以可以使用 babel-preset-env ,仅转义尚未被我们的目标浏览器支持的那些 ES2015 + 特性。并且分开构建,这样就形成了两份构建文件,针对不同版本的浏览器加载不同的文件,避免多余代码影响加载速度。

是否使用了 JavaScript 差异化服务?

编译和提供两个独立的 JavaScript 包:“常规”构建,带有 Babel-transforms 和 polyfill 的构建,只提供给实际需要它们的旧浏览器,以及另一个没有转换和 polyfill 的包(具有相同功能)。

通过增量解耦识别和重写遗留代码
  • 老项目充斥着陈旧和过时的代码。重新查看你的依赖项,评估重构或重写最近导致问题的遗留代码所需的时间。
  • 一旦你了解了遗留代码的影响,就可以从增量解耦开始。
  • 设置指标,跟踪遗留代码调用的比率是保持不变或是下降,而不是上升。公开阻止团队使用该库,并确保你的 CI 能够警告开发人员,如果它在拉取请求(pull request)中使用。Polyfill 可以帮助将遗留代码转换为使用标准浏览器功能的重写代码库。
识别并删除未使用的 CSS/JS

查找未使用的 CSS/JS代码,删除它们

减小 JavaScript 包的大小
  • 当你只需要一小部分时,你很可能会发送完整的 JavaScript 库,以及提供给不需要它们的浏览器的过时 polyfill,或者只是重复代码。
  • 考虑使用 webpack-libs-optimization,在构建过程中删除未使用的方法和 polyfill。
是否使用了 JavaScript 代码块的预测预获取?

介绍了使用Guess.js,它的数据来确定用户最有可能从给定页面访问哪个页面。根据从 Google Analytics 或其他来源收集的用户导航模式,Guess.js 构建了一个机器学习模型,用于预测和预获取每个后续页面中所需的 JavaScript。 这样我们就可以根据用户当前访问的页面和操作,提前加载下一步资源。(高大上)

从针对你的目标 JavaScript 引擎进行优化中获得好处

研究哪些 JavaScript 引擎在你的用户群中占主导地位,然后探索针对这些引擎的优化方法。(幸好我们的产品提示用户最好用谷歌浏览器,要不就泪奔了)

使用客户端渲染还是服务器端渲染?
  • 我们的目标应该是设置渐进式启动:使用服务器端渲染来获得快速的首次有效绘制,但也包括一些最小的必要 JavaScript,以保持首次交互时间接近首次有效绘制时间。
  • 考虑使用 webpack 的动态 import() 支持,延迟加载 UI 的部分,降低加载、解析和编译成本,直到用户真正需要它们
  • 如果用户没有明显的好处,客户端渲染可能不是真正必要的 —— 实际上,服务器端渲染的 HTML 可能更快。也许你甚至可以使用静态站点生成器预渲染一些内容,并将它们直接推送到 CDN,并在顶部添加一些 JavaScript。
约束第三方脚本的影响
  • 第三方脚本指标不受最终用户体验的影响,因此通常一个脚本最终会调用令人讨厌的冗长的第三方脚本,从而破坏了专门的性能工作。
  • 考虑使用 service worker,通过超时竞争资源下载,如果资源在特定超时内没有响应,则返回空响应以告知浏览器继续解析页面。
  • 你还可以记录或阻止未成功或不符合特定条件的第三方请求。
  • 另一种选择是建立内容安全策略(CSP)以限制第三方脚本的影响,例如:不允许下载音频或视频。
设置 HTTP 缓存标头

仔细检查是否已正确设置 expires、max-age、cache-control 和其他 HTTP 缓存头。(灵活运用浏览器的缓存,珍惜这个好功能)

第六部分:交付优化

是否所有的 JavaScript 库都采用了异步加载?
  • 对脚本执行此操作使用 HTML 中的 defer 和 async 属性,延迟脚本加载优先渲染页面
  • 更倾向于使用 defer
使用 IntersectionObserver 加载大型组件

使用Intersection Observer API延迟加载所有大型组件。它允许我们添加一个观察对象,当这个对象发生变化时,通过回调函数去加载我们需要的组件,这就保证了大型组件只会在必要的时候加载,不会影响其它流程的加载渲染速度。

渐进式加载图片

可以通过向页面添加渐进式图像加载技术将延迟加载提升到新的水平。 主要是可以先加载质量较差甚至模糊的图像,然后在页面继续加载时,其替换为原图。

你是否发送了关键的 css?

为了确保浏览器尽快开始渲染页面,通常做法是收集开始渲染页面的第一个可见部分所需的所有 CSS(称为“关键 CSS”或“首页 CSS”)并将其以内联的形式添加到页面的 “” 中,从而减少往返请求。

尝试重组 CSS 规则
  • 将主 CSS 文件拆分为单独的媒体查询。这样,浏览器将检索具有高优先级的关键 CSS,以及其他具有低优先级的所有内容 —— 最终完全脱离关键路径。
  • 避免将< link rel="stylesheet" /> 放在 async 标签之前。
  • 使用 service worker 缓存内联 CSS 文件
你有没有将请求设为 stream?

在任何给定的时间里,内存中可能只有一部分数据块可用,但 Streams提供了一个读或写异步数据块的接口,它们允许发出原始请求的页面在第一块数据可用时立即开始处理响应,并使用针对流优化的解析器逐步显示内容。

考虑使组件具有连接感知能力

是组件能够感知用户的设备及网络性能,定制性的加载内容,比如:你可以将高 DPI 图像的请求重写为低 DPI 图像请求,删除 Web 字体、花哨的视差效果、预览缩略图和无限滚动、关闭视频自动播放、服务器推送、减少显示项目的数量并降低图像质量。

考虑使组件具有设备内存感知能力

使用Device Memory API(Chrome63+)动态地根据可用设备内存调整资源。

做好连接的热身准备以加速交付

合理使用 preconnectdns-prefetch。 dns-prefch节省在后台执行 DNS 查找的时间。preconnect 要求浏览器在后台启动连接握手(DNS、TCP、TLS)。

使用 service workers 进行缓存和网络降级

网络上的任何性能优化都赶不上从用户计算机上本地存储的缓存中取数据快。所以:

  • 静态资源缓存到 service worker 缓存中
是否在 CDN/Edge 上使用了 service workers,例如,用于 A/B 测试?
  • 使用 CDN 服务器上的 service worker 来调整边缘性能。
优化渲染性能
  • 使用CSS容器隔离开销大的组件
  • 度量运行时渲染性能
是否优化了渲染体验?

我们不应低估用户心理感知性能的作用。 所以:

  • 就需要感知管理抢先启动提前完成容忍度管理开始发挥作用。
  • 我们可以测试框架屏幕(实现演示),而不是loading指示器。
  • 添加过渡/动画,简单的欺骗用户体验。
  • ......

第七部分:HTTP/2

迁移到 HTTPS,然后启用 HTTP/2

HTTP/2 现在已经得到了很好的支持;它没有任何大的改变;并且在大多数情况下,使用它会让你得到出色的性能表现。一旦在已经 HTTPS 运行了,你可以使用 service workes 和 server push 得到巨大的性能提升(至少长期来看)。

合适地部署 HTTP/2

为让资源通过 HTTP/2 传递需要对现在提供资源的方式进行部分修改。

  • 在打包成一个大模块和并行加载许多小模块之间找到合适的平衡。
  • 考虑渐进式加载 CSS
你的服务器和 CDN 支持 HTTP/2 吗?

不同的服务器和 CDN 可能可能对 HTTP/2 的支持不一样。需要检查你的配置,或快速查找服务器的运行情况以及可以支持的功能。选择合适的用法。

您的服务器和cdn是否支持HTTP over QUIC(HTTP/3)?

如果你想冒险或者走在技术的前沿,那么试试HTTP over QUIC(HTTP/3)。
虽然HTTP/2带来了显著的改进,但在网络速度慢或不可靠(严重的数据包丢失)的情况下,它的性能并不是特别好。 QUIC和HTTP/3更好更安全:更快的握手、更好的加密、更可靠的独立流、更加密。

OCSP Stapling 是否启用?

通过在你的服务器上启用 OCSP Stapling,可以加速 TLS 握手。

你采用 IPv6 了吗?

主要的手机网络正在迅速接受 IPv6(美国已经达到 50% IPv6 采纳率),更新你的 DNS 为 IPv6 是一个不错的想法,这样在将来可以保持服务器安全稳固。

是否使用 HPACK 压缩?

如果你在使用 HTTP/2,请确保检查你的服务器为 HTTP 响应头实现了 HPACK 压缩来减少不必要的载荷。

确保你的服务器安全稳固

所有浏览器的 HTTP/2 实现都是运行在 TLS 之上,所以你可能想避免安全性警告或页面中的某些元素出错。

第八部分:测试和监控

你优化过你的审计流程吗?
你测试过代理和过时的浏览器吗?

光测试 Chrome 和 Firefox 还不够。看看你的网站在代理浏览器和过时浏览器中的表现。

你测试过辅助工具的性能吗?

浏览器有很多辅助性的功能插件,有可能会影响我们的网站运行,我们不能保证用户是否安装了它,所以要针对这部分工具进行测试。

是否建立持续监控?

建立一个持续有效的监控是保证网站暴露问题持续优化的重点。可持续监控工具能自动报警,给你更详尽的性能画像。

最后:速效方案

本文的清单相当全面,并且完成所有的优化需要相当一段时间。所以,如果你只有一小时但想获得巨大性能提升,你要怎么做?让我们总结为 12 条易于实现的目标。显然,在你开始之前和完成之后,评估结果,包括在 3G 和有线网络连接下的渲染时间和 Speed Index。

  1. 评估实际经验和设置合适的目标。一个很好的目标是追求首次有意义的渲染时间 < 1 秒,同时 Speed Index < 1250 秒,慢速 3G 网络下首次可交互时间 < 5秒,TTI < 2 秒。针对渲染时间和首次可交互时间做优化。
  2. 为你的主要模板准备关键 CSS,并在放在页面的 head 标签内(预算应小于 14 KB)。对于 CSS/JS,使它们小于关键文件大小最大预算 gzipped 压缩后为 170 KB(未压缩为 0.7 MB)。
  3. 尽可能地让更多的脚本分割,优化,defer 加载或者懒加载,检查轻量级的可选包并限制第三方包的大小。
  4. 使用< script type="module">来让代码只对旧浏览器工作。
  5. 试着整合 CSS 规则并测试 in-body CSS。
  6. 使用更快的 dns-lookup,preconnect,prefetch 和 preload 来添加资源提示来加速分发。
  7. 给网络字体分组并异步加载,在 CSS 中利用 font-display 来加速首次渲染。
  8. 优化图片,并考虑为重要的页面(例如首页)使用 WebP。
  9. 检查 HTTP 头设置的缓存并确保已经被合适地设置。
  10. 只要服务器运行在Linux内核版本4.9+上,就启用TCP BBR拥塞。
  11. 在服务器上启用 Brotli 和 Zopfli 压缩。(如果不能,别忘了启用 Gzip 压缩。)
  12. 如果 HTTP/2 可用,启用 HPACK 压缩并开始监控 mixed-content 警告。如果HTTP/3在CDNs上可用,则启用HTTP/3。
  13. 在 service worker 中缓存字体,样式,JavaScript 和图片等资源文件。
  14. 如果可能,启用OCSP装订和IPv6。

后续我会针对里面一些细节去实践相关方案,分享给大家,可以关注我的公众号,获取推送。




原文地址:访问原文地址
快照地址: 访问文章快照