前言
页面的性能优化对于前端来说永远是离不开的课题,前端性能优化一直也不是作为一个单独的问题存在,它往往需要开发者结合
计算机网络
、
浏览器相关技术
、
前端框架
、
构建工具
以及
开发者自己的代码
等多层面去思考优化的方案,所以前端性能不应该是前端领域的一个孤岛⛱️,而需要作为串联起前端技术的零件🔩。
如何去进行性能优化?
一说到前端性能优化,可能大家一开始的想法就是
压缩页面产物大小
、
图片换成雪碧图
、
资源懒加载
等一系列方式,但是要知道性能优化并不是直接套用方法论的,性能优化的前提清楚需要页面需要优化的指标,并根据有方向有目的进行优化。
确立优化的指标
古早时期,大家常常会把性能优化与
页面白屏时间
划上等号,但是白屏时间短并不代表页面性能就好,例如页面进去是渲染的是
loading动画
又或者是一个
可有可无的模块
,虽然白屏时间是短了,可以渲染出来的内容对用户来说是毫无意义的,美其名曰提升了首屏性能,实际上不过是掩耳盗铃罢了。
对大多数页面来说,性能优化的的宗旨还是确保用户可以尽快的看到
有用的页面信息
,那页面加载到什么程度才能够算是渲染出有用的页面信息呢?
FMP
FMP(first meaningful print)
即首次有意义的渲染,这个指标感觉真的和我们所说的
有用的页面信息
是完美契合。
我们可以看到在FMP的阶段,页面中大部分元素其实都被渲染出来,我们来看看这个值到底是怎么被计算的。
image.png
在页面渲染和解析的过程中,
布局对象会被逐步添加至布局树中
,从上图可以看出布局对象的数量和页面完成度是高度相关的,所以业界比较认可的计算方式是页面在加载和渲染过程中最大布局变动之后的绘制时间作为当前页面的
FMP
,上图对应的
FMP的值为1.907s
。
通常的检测手段是使用
MutationObserver
监听页面整体的
DOM
变化,然后通过计算变化比例,找到
DOM
变化幅度占比最高的时间点。
FMP需要浏览器支持
MutationObserver API
,并且
FMP
的计算过程是十分复杂的。在我们团队开发的时候会比较hack,页面直接上报的是首屏接口被处理后
React setState
后的时间或者是
首屏大图渲染
的时间,而其实这个时间并非是页面精准的FMP值。
在
lighthouse 6.0
的性能规范中,废弃了
FMP
的这个指标。官方给到的解释主要有两点:
在生产环境中,
FMP
对页面的微小变化过于敏感,很容易导致结果不一致。
该指标的定义很大程度上依赖于浏览器的具体实现细节,缺乏可供参考的标准化。
LCP
相比于
FMP
计量的
复杂和不确定性
,W3C性能小组和Google研究发现,衡量页面主要内容加载更准确方法是查看最大元素的呈现时间。也就是
lighthouse
的指标
LCP
。
LCP(Largest Contentful Paint)
指的是视口中可见最大图片或文本块的渲染出来的时间。所谓的最大图片或文本块包含以下内容:
元素、
元素中的
元素。
如何优化LCP指标
由于LCP相关的优化涉及的内容比较多,可以翻阅我的另一篇文章
LCP优化的最佳实践
[1]
。
但是一个真正用户体验好的页面并不仅仅只是页面
LCP
数值好就代表真的体验好,根据最新
Google
最新的页面性能的核心指标标准显示,除了LCP外,还包括互动响应指标
INP(Interaction to Next Paint)
以及布局偏移指标
CLS(Cumulative Layout Shift)
,这三个指标共同组成了页面核心性能指标。
image.png
(LCP)
:衡量加载性能。为了提供良好的用户体验,LCP 必须在网页首次开始加载后的
2.5 秒
内发生。
(INP)
:衡量互动体验。为了提供良好的用户体验,网页的 INP 不得超过
200 毫秒
。
(CLS)
:衡量视觉稳定性。为了提供良好的用户体验,必须将 CLS 保持在
0.1.
或更低。
INP(Interaction to Next Paint)
INP(Interaction to Next Paint)
是使用
Event Timing API
中的数据来评估网页响应能力。INP 会在网页生命周期内观察用户与网页进行的所有点击、点按和键盘互动的延迟时间,并报告最长持续时间。
INP
大家可能会感受到陌生,可能更熟悉的指标是
FID(First Input Paint)
,
FID
用户首次与页面交互(例如点击链接、按钮等)到浏览器实际能够开始处理事件处理器之间的时间。2年前这个指标还是lighthouse检测页面交互性的核心指标,如今将被
INP
取代了。
INP和FID的区别
INP
会统计网页生命周期内观察用户与网页进行的所有点击、点按和键盘互动的延迟时间。而
FID
仅测量了页面上首次互动的输入延迟,所以我认为可以等价理解为
FID
像是成为了
INP
的子集。
对于纯渲染的页面来说,其实
INP
记录的交互时间的最大值可能就是
FID
,因为此时大量的JS执行,以及DOM生成,以及样式渲染,此时
主线程肯定会被长时间占用的
。所以对于纯渲染的页面可能考虑首屏的
INP
指标。但是对于一些有复杂交互的页面,例如文本编辑器或联动效果的的表单页面,可能首屏内容渲染是很快的,但是可能后续的互动时间会比较耗时,所以此时FID是不能衡量互动性能的,此时INP更能体现页面的交互性能。
INP指标怎么计算的?
首先INP指标的总耗时主要包括三个方面:
输入延迟,从用户发起与网页的互动开始,在互动的事件回调开始运行时结束。
互动效果在浏览器下一帧呈现效果的时间。这三个阶段组成了INP统计的互动耗时,若
INP小于200ms
,页面的互动性能可以被认为很好。
如何优化INP的指标
减少非首屏的JS资源的加载和执行
,减少JS对主线程的占用时间,提升首屏响应速度。
优化互动事件的回调,尽可能让出页面的主线程,优先完成优先级高的任务回调
。
这里可以借用web.dev网站提供的一个
案例
[2]
了解下如何在日常开发中让主线程,拆分掉冗长的事件回调。
同步布局:
强制同步布局是指在执行 JavaScript 或者 CSS 动画过程中,代码强制浏览器进行布局计算(Reflow),然后再读取某些样式信息。例如,如果一个 JavaScript 函数对 DOM 进行修改后立即读取某些样式属性(如元素的偏移量或尺寸),浏览器必须先完成布局计算,以确保返回的信息是最新的。这种强制的布局过程可能会导致显著的性能瓶颈,因为它阻塞了主线程,直到布局计算完成。
布局抖动:
布局抖动通常是由于代码在一次事件循环中多次读写 DOM 属性而导致的连续布局计算。每次读取或写入都可能导致布局的重新计算,如果这些操作在循环或频繁的函数调用中进行,就会导致大量的计算开销,从而降低页面性能。
尽可能
减少DOM的数量和深度
,降低DOM重新渲染所造成的性能影响。
CLS
CLS(Cumulative Layout Shift)是用于衡量视觉稳定性的重要指标,它有助于量化用户遇到意外布局偏移的频率。
CLS是如何计算的?
首先我们看看CLS的计算公式:
CLS 值 = 偏移比例(偏移的距离占视窗的距离的比例)* 元素比例(元素高度占视窗的高度的比例)
接着我们根据一个具体案例了解下CLS的计算过程:
(1) 起初页面加载出了粉色的一个div块。
(2)之后黄色模块再加载出来。
这是后线出案的楼块.png
我们假定粉色模块的高度占视窗的
50%
,所以元素比例为
0.5
,然后假定黄色模块占视窗的
20%
,其造成的偏移距离也是
20%
,所以偏移比例为
0.2
。最终CLS的值等于
0.5 * 0.2 = 0.1
。CLS低于
0.1
可以代表视觉稳定性比较好。