Web 网站性能优化
消费者们越来越依赖移动设备来访问数字内容和服务,如果你查看你的网站分析,你可能会在自己的数据中看到这个故事。
消费者的要求也比以往任何时候都高,当他们在你的网站上衡量体验时,他们不仅仅是在将你与竞争对手进行比较,他们还在根据他们每天使用的一流服务对你进行评分。
提升性能的作用是什么呢?可以说:
⭐ 性能在于留住用户
⭐ 性能在于提高转化率
⭐ 性能关乎用户体验
⭐ 性能以人为本
这篇文章总结了一些关于 如何提升一个 Web 网站的性能相关的大量提示和技巧。
现在,让我们开始!👏👏👏
1 网页性能优化指标
测试工具
对公网上的网页进行 FCP 测试的工具有:
- PSI (PageSpeed Insights):会针对网页在移动设备和桌面设备上的用户体验生成报告,并就如何改进网页提出建议 PageSpeed Insights
- webpagetest:可提供有关页面在各种条件下的性能的深入诊断信息 webpagetest
在本地调试的时候可以使用
DevTools
的lighthouse
来查看WebPageTest
核心 Web 指标
适用于所有网页的 Web 指标子集,每位网站所有者都应该测量这些指标,并且这些指标还将显示在所有 Google 工具中,包括:
⭐LCP ➡️ Largest Contentful Paint
- 最大内容渲染时间
- 从页面开始加载到视窗内 最大内容 绘制的所需时间,测量 加载性能
⭐CLS ➡️ Cumulative Layout Shift
- 累计布局位移
- 比较单个元素在帧与帧之间的位置偏移,测量 视觉稳定性
⭐FID ➡️ First Input Delay
- 首次输入延迟
- 测量 交互性
FCP ➡️ First Contentful Paint
⚠️不包含 在 核心 Web 指标 中,但是以下 PSI 指标和 lighthouse 指标都包含。因为只会捕获加载体验最开始的部分,如果某个页面显示的是一段启动画面或加载指示,那么这些时刻与用户的关联性并不大。
- 首次内容绘制
- 是浏览器在您的页面上呈现 第一个 DOM 元素 的时间。这包括图像、画布元素(非白色)或文本。通俗地说,FCP 是指用户可以看到页面的某些部分发生变化。
PSI 工具指标
除以上核心指标之外还包括:
INP ➡️ Interaction to Next Paint
INP 与首次输入延迟 (FID) 的不同:INP 考虑 所有页面交互,而 FID 仅考虑第 一次 交互。它还仅测量第一次交互的 输入延迟,而不是运行事件处理程序所需的时间或呈现下一帧的延迟。
- 下次绘制交互
- 通过观察用户访问页面的整个生命周期中发生的所有单击、敲击和键盘交互的延迟来评估页面对用户交互的整体响应能力。低 INP 意味着页面始终能够快速响应所有(或绝大多数)用户交互,将于 2024 年 3 月取代首次输入延迟 (FID)。 。
TTFB(experimental) ➡️ Time to First Byte
- 首字节时间
谷歌控制台 lighthouse 指标
除以上核心指标之外还包括:
- SI ➡️ Speed Index
- 速度指数
- 反映网页内容填充的速度
- TBT ➡️ Total Blocking Time
- 阻塞总时间
- 反映用户的交互是否能及时响应
2 具体介绍与优化方案
LCP
❓️ 什么是 LCP
LCP 最大内容绘制 会根据页面 首次开始加载 的时间点来报告 可视区域内 可见的 最大图像或文本块 完成渲染的相对时间。
最大内容绘制考量的元素
<img>
元素- 内嵌在
<svg>
元素内的<image>
元素 <video>
元素(使用封面图像)- 通过
url()
函数(而非使用 CSS 渐变)加载的带有背景图像的元素 - 包含文本节点或其他行内级文本元素子元素的块级元素。
元素大小的计算方式
用户在 视口 中可见的元素的大小。如果元素延伸到视口之外,或者任何元素被剪裁或具有不可见的溢出,则这些部分不会计入元素的大小。
- 如果图片缩放,尺寸以较小者为准
- 文本元素,仅考虑其文本节点的大小
- 所有元素,不考虑通过 CSS 应用的任何边距、填充或边框
<svg>
元素目前不被视为 LCP 候选者- 使用
background-image
插入的背景图不会被视为最大内容绘制元素
💡 如果当前最大内容元素的元素从视口中删除(甚至从 DOM 中删除),它将仍然是最大内容元素,除非渲染更大的元素。
💡 对元素大小或位置的更改不会生成新的 LCP 候选对象,只有元素在可视区域内的初始大小和位置会被纳入考量范围。
LCP 分发和报告最大内容的过程
网页通常是分阶段加载的,因此,在加载开始到加载完成的这几帧中,页面上的最大元素也可能会发生变化。
例如,在一个带有文本和首图的网页上,在界面上的较大图片元素尚未 “ 渲染完成 ” 的时候,较小的先渲染完成的文本元素将会被视为 第一个 最大内容绘制元素,浏览器此期间会分发一个largest-contentful-paint
条目给这个文本元素,用于识别最大内容元素。随后,一旦首图完成加载,浏览器就会分发第二个largest-contentful-paint
条目。如果这个界面还有其他元素,有任意一个新元素大于先前的最大内容元素,则浏览器还将报告一个新的PerformanceEntry
条目。
最大内容绘制元素频繁变化的影响
计算和分发新性能条目的性能开销较高时:
会导致用户等待时间增加,出现例如图片加载不完整或动画无法流畅播放的情况
会导致交互延迟,用户点击按钮或执行其他操作时的响应时间会增加
页面加载期间用户无法操作,对搜索引擎优化(SEO)有一定影响
LCP 示例
最大内容元素从顶部的文本元素,变更为渲染完成之后的图片元素,LCP 发生变化
最大内容元素是一串文本元素,随着布局的改变被从可视区域中被移除,但是依旧是最大内容元素,直到被更大的图片元素取代,LCP 发生变化
延迟加载方案
第一张图为 svg 元素不会被算作最大内容元素,第二张图的图片在渲染中状态,不能计算。
随着图片渲染完成,图片元素成为第一次的最大内容元素,之后的元素都在图片之后渲染完成,但都没有图片元素大,所以图片是依旧是界面内的最大内容元素
当用户与页面进行交互(通过轻触、滚动或按键)时,浏览器将立刻停止报告新条目,因为用户交互通常会改变用户可见的内容(滚动操作时尤其如此)。
所以在进行搜索交互之后,最大内容元素为图二这段文本,文本在所有图像或标志完成加载之前就显示了出来。由于所有单个图像都小于这段文字,因此这段文字在整个加载过程中始终是最大元素。
🌟🌟 如何优化
- 使用 PRPL 模式实现即时加载
- 推送 (Push)(或预加载)最重要的资源。
- 尽快渲染 (Render) 初始路线。
- 预缓存 (Pre-cache) 剩余资产。
- 延迟加载 (Lazy load) 其他路线和非关键资产。
- 优化关键渲染路径
- 优化阻塞渲染的 JS 和 CSS
- 改进服务器处理内容的方式和位置
- 使用 CDN 加速
- 优先使用缓存提供 HTML 页面
- 尽早建立第三方连接
- 优化最大内容绘制考量的元素
- 优化和压缩图像
- 预加载重要资源
- 压缩文本文件
- 基于网络连接交付不同资产(自适应服务)
- 使用 Service Worker 缓存资产
- 优化渲染方式
- 使用服务端渲染(会影响 TTFB 和 TTI)
- 使用预渲染
- 最小化关键 JS(压缩 JS 代码,延迟加载未使用的 JS)
CLS
❓️ 什么是 CLS
CLS 累积布局偏移 是指一个可见元素的位置从一个已渲染帧变更到下一个已渲染帧,就发生了 布局偏移,页面内容的意外移动通常是由于异步加载资源,或者动态添加 DOM 元素到页面现有内容的上方造成的。
💡 只有当现有元素的 起始位置 发生变更时才算作布局偏移。如果将新元素添加到 DOM 或是现有元素更改大小,则不算作布局偏移,前提是元素的变更不会导致其他可见元素的起始位置发生改变。
🌟🌟 如何优化
- 使用动画和过渡
- 始终在图像和视频元素上包含尺寸属性,或者通过使用CSS 长宽比容器之类的方式预留所需的空间
- 除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容
FID
❓️ 什么是 FID
FID 首次输入延迟 将用户尝试与无响应页面进行 交互 时的体验进行了量化,测量从用户 第一次 与页面交互直到浏览器对交互作出响应,并实际能够开始处理事件处理程序 所经过的时间。低 FID 有助于让用户确信页面是有效的。
是否正在发生? | 导航是否成功启动?服务器有响应吗? |
---|---|
是否有用? | 是否渲染了足够的内容让用户可以深入其中? |
是否可用? | 页面是否繁忙,用户是否可以与页面进行交互? |
是否令人愉快? | 交互是否流畅自然,没有延迟和卡顿? |
⚠️ INP 将于 2024 年 3 月取代首次输入延迟 (FID)
⚠️ 和 INP 一样会存在不展示 FID 的情况
哪些算是首次输入
FID 只关注不连续操作对应的输入事件,如点击、轻触和按键。其他诸如滚动和缩放之类的交互属于连续操作,具有完全不同的性能约束(而且,浏览器通常能够通过在单独的线程上执行这些操作来隐藏延迟)。
🌟🌟 如何优化
- 分割长任务
- JS 代码优化,压缩代码体积
- 代码分割(按需加载)
- 对关键路径或首屏内容不需要的脚本使用 async 或 defer
- 最大限度减少未使用的 polyfill
- 将更多逻辑转移到服务器端,或在构建期间静态生成更多内容
- 减少数据获取依赖:
- 尽量最大限度地减少对级联数据获取的依赖
- 尽量最大限度地减少需要在客户端进行后处理的数据量
- 优化第三方脚本执行
- 按需加载第三方代码
- 延迟加载
- 使用 Web Worker
FCP
⚠️ 只会捕获加载体验中最开始的部分
❓️ 什么是 FCP
FCP 首次内容绘制 是衡量用户感知的重要的加载性能指标,关注的是 第一个 内容元素(文本、图像、背景图像、<svg>
元素或非白色的<canvas>
元素)的 渲染时间 反映了页面加载速度对用户可见内容的影响。
FCP 发生在第二帧,因为那是首批文本和图像元素在屏幕上完成渲染的时间点
🌟🌟 如何优化
⚠️ 大部分和 LCP 的优化方式相同
- 减少服务器响应时间 (TTFB)
- 避免多个页面重定向
- 避免巨大的网络负载
- 使用高效的缓存策略服务静态资产
- 确保文本在网页字体加载期间保持可见
- 保持较低的请求数和较小的传输大小
INP
❓️ 什么是 INP
INP 下次绘制交互 观察用户与页面进行的 所有交互 的 延迟,并报告所有(或几乎所有)交互都低于的单个值。低 INP 意味着页面始终能够快速响应所有(或绝大多数)用户交互。INP 的目标是确保对于用户进行的所有或大多数交互,从用户发起交互到绘制下一帧的时间尽可能短。
不报告 INP 的情况
- 页面已加载,但用户从未单击、点击或按下键盘上的按键
- 页面已加载,但用户使用不涉及单击、点击或使用键盘的手势与页面进行交互,例如滚动或悬停
- 该页面正在由机器人(例如搜索爬虫或无头浏览器)访问,但尚未编写脚本来与该页面交互
🌟🌟 如何优化(JS)
优化输入延迟:
- 避免重复计时器启动过多的主线程工作
- 避免长时间的任务
- 注意用户交互的重叠,可对输入进行去抖动,注意 JavaScript 中的动画可能会引发许多requestAnimationFrame调用,这可能会妨碍用户交互,尽可能使用 CSS 动画,避免非合成动画
优化事件回调:
- 分解长任务,不要阻塞主线程
当任务被分解时,浏览器有更多机会响应更高优先级的工作,其中包括用户交互
- 分解 JS 代码为更小的功能,合理使用
setTimeout()
等可以将代码执行推迟到后续任务的 API - 使用
async
/await
来创建让出点 - 在必要的时候使用
isInputPending()
来让出主线程
最大限度地减少演示延迟
- 保持初始 DOM 较小
- 尽量少的使用 JavaScript 渲染 HTML
TTFB
❓️ 什么是 TTFB
TTFB 第一字节时间 是一个衡量对资源的请求和响应的第一个字节开始和到达之间时间的指标。TTFB 的衡量要快于 FCP 和 LCP,也就是从打开页面开始,到加载出页面的那一段等待时间。
网络请求阶段及其相关时间损耗的图示. TTFB 测量
startTime
和responseStart
之间的时间损耗
TTFB 是下列请求节点的时间损耗汇总:
- Redirect time 重定向时延
- Service worker 启动时延(如果适用)
- DNS 查询时延
- 建立连接和 TLS 所消耗时延
- 请求,直到响应的第一个字节到达为止的时延
减少 连接建立 和 后端服务的时延 将有助于降低 TTFB。
⚠️ 注意:TTFB 并不是核心 Web 指标,如果并不太影响 Web 核心指标,优化需求并不是很高
🌟🌟 如何优化 TTFB
TTFB 的值会过高,在很大程度上取决于 托管供应商 和 后端服务,通常通过选择一个合适的托管供应商是优化 TTFB 的高效方案 ,其基础设施可确保高正常运行时间和响应能力,与高级 CDN 服务相结合,效果会更好。同时后端服务的数据库垃圾过多,或者查询次数过多等,也会影响 TTFB 的数值。
- 避免多次重定向.
- 使用
<link rel="preconnect">
标签,提前向跨域资源源建立预连接。 - 使用 HSTS 预加载列表,以消除 HTTP 转 HTTPS 的重定向延迟。
- 使用 HTTP/2 or HTTP/3,以提供更高的网络性能和效率。
- 考虑预测性预取:使用
<link rel="prefetch">
或<link rel="dns-prefetch">
标签,为没有指定减少数据使用偏好的用户提供快速页面导航。 - 在可能和适当的情况下,使用服务器端生成(SSG)来代替服务端渲染(SSR)的标记。
SI
❓️ 什么是 SI
SI 速度指数 是衡量页面加载期间内容视觉显示的速度指标,以秒为单位。SI 指标的计算是通过模拟用户在页面加载过程中不断更新视图的情况来实现的。
它考虑了整个页面的 可见区域 以及 内容加载的顺序和时间,以及 渲染完成的时间。SI 分数越低,说明页面越快呈现出有意义的内容。
🌟🌟 如何优化
- 分解长任务
- 优化 JS 代码(压缩、分割、删除,使用PRPL模式)
- 确保文本在网络字体加载期间保持可见
TBT
❓️ 什么是 TBT
TBT 总阻塞时间 测量页面被阻止响应用户输入(例如鼠标单击、屏幕点击或键盘按下)的总时间。总和是通过添加 FCP 首次内容渲染 和 Time to Interactive 页面资源加载成功并能响应用户交互的时间点(已退役指标)之间所有长任务的阻塞部分来计算的。
任何执行时间超过 50 毫秒的任务都是长任务。50 毫秒之后的时间量是阻塞部分。例如,如果 Lighthouse 检测到一个 70 毫秒长的任务,则阻塞部分将为 20 毫秒。
🌟🌟 如何优化
- 分解长任务
- JS 代码优化(压缩、分割、重构、删除,合理加载第三方 JS 代码)
3 案例
- 使用浏览器的控制台打开 lighthouse,查看本地 3000 端口网站的性能情况
lighthouse 报告总共五个重要性能指标,此网站 TBT 性能中等
- 查看 TBT 详细解析
指出以下两个长任务导致 TBT 时间延长
⁉️ 指出问题:
js 文件太大
🌟 建议优化:
对 JS 代码进行压缩、分割、重构、删除,合理加载第三方 JS 代码等方式来优化
- FCP 指标为快速,但是有可优化的地方
报告指出发现一个链式请求,是一个 css 文件,这个文件是以高优先级加载的,回导致加载时间过长
⁉️ 指出问题:
css 文件太大
🌟 建议优化:
对 css 代码进行分解或删除部分重复代码
- LCP 指标为快速,但是有可以优化的地方
此处指出该 img 是视口内的最大绘制元素,但是却被延迟加载了,会导致 LCP 的延迟
⁉️ 指出问题:
视口最大绘制元素延迟加载了
🌟 建议优化:
优化关键渲染路径,LCP 元素不用延迟加载
此处指出页面使用了大量的网络负载,总大小为 3,510 KiB,它提醒大型网络负载会导致用户产生实际的费用,并且与长加载时间高度相关
⁉️ 指出问题:
图片和文件太大
🌟 建议优化:
进行资源压缩、代码分割、资源缓存、延迟加载、使用现代图像格式等方式来优化