面试被问到性能优化,我们可以讲些什么
大厂技术 高级前端 Node进阶
点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群
到本文结合谷歌官方工具 Lighthouse,分析了最新的前端页面性能评分标准,帮助大家更好地理解各项性能指标,以提升并优化相关的前端项目。
一、前端页面性能及其分析工具
前端页面的性能,一直都是大家持续关注的一个领域,因为用户的留存率和页面加载性能息息相关。根据google做出的数据统计,页面访问时长从1s增加到3s,用户跳出率增加32%。
对于前端页面性能的评估,一般是两种形式:一种是使用性能分析工具,在线对网页各项指标进行打分评估;一种是使用性能监控,通过 performance api 或者自定义的埋点上报用户网络真实的访问情况,然后进行统计分析。
虽然通过统计用户的数据更加真实,但是为了对页面性能能够有一个统一的量化标准,我们往往选择使用标准的打分工具对页面的性能进行评估。
性能分析初期,我们会从 chrome 的开发者工具对网页进行分析,包括查看 load 和 DOMContentLoaded 等事件触发的时间。后来又有了一系列的性能分析工具,例如 webpage analyzer、WebPageTest、Yslow等。
现在由于google官方已经将他们自己开发的 Lighthouse 嵌入到开发者工具的选项卡中,因此我们就把 Lighthouse 当做是标准评估工具。
Lighthouse 是一款开源的web页面性能分析工具,并且会给出页面最佳实践的一些相关建议。除了可以直接在 chrome DevTools 中使用外,也支持使用浏览器插件(chrome和Firefox)或者npm包(node api或者CLI)。
像google的网页 measure以及 PageSpeed Insight工具都是调用的 Lighthouse 对页面进行分析。
二、如何为页面的性能打分
1. Lighthouse 的迭代与性能指标变化
Lighthouse 第一个开源的版本可以追溯到2016年,目前(2020年10月)最新的版本是6.4.1,已经迭代89个版本。而在这几年中 Lighthouse 对于性能指标的选取也一直在更新。
google在最新的6.X版本中,相比于5.X版本,更新了三个新的性能指标:去掉了 FMP(First Meaningful Paint)、FCI(First CPU Idle) 和 mpFID(Max Potential First Input Delay);
加入了 TBT(Total Blocking Time)、LCP(Largest Contentful Paint) 和 CLS(Cumulative Layout Shift)。后文会针对这些指标进行详细的讲解。
之前5.X版本的Lighthouse
现在的Lighthouse(6.X版本)
2. 如何计算页面性能分数
如下图所示,在页面性能部分,Lighthouse 会综合目前的6个关键指标的表现情况,计算出页面的性能分数。
以最新的6.X计算方法来看,每个性能指标的数值会对应一个该指标的分数。例如上图中的FCP、SI、LCP、TTI、TBT、CLS等数值,对应的单项分数就依次为78、62、37、5、99、92分。一般来说数值越小该指标分数越高。
而这6个指标对应的权重分别为15%、15%、25%、15%、25%、5%,通过加权平均计算出性能总分为图中的60分。
如果想知道每个指标数值与其对应分数的具体计算方法,可以参考文章末尾的资料5和6。
三、关键的性能指标
在6.0版本的 Lighthouse 中,被去掉的关键性能指标分别是FMP(首次有意义的渲染帧)、FCI(首次CPU空闲)以及用户mpFID(潜在最大首次输入延迟)。
下面我们从这三个被废弃指标的定义开始切入,更好的理解现在版本的指标选取依据。
1. 什么是 FMP,和 FCP 有什么区别
说起FMP之前,我们必须要先介绍一下 First Contentful Paint(FCP):首次内容渲染时间。
如其名所示,只要首次触发了浏览器的 The First Page Paint 事件,此刻的时间点就是FCP。但此时渲染的不一定是重要的页面信息,比如仅仅是绘制了一个头部的 action bar 等,甚至不一定会渲染出可见的元素。虽然在Lighthouse6.0中得到了保留,但对性能得分的权重从23%降低到15%。
因此,FCP 不能作为一个从用户视角准确衡量页面性能好坏的指标。
在这个背景下,FMP(First Meaningful Paint:首次有意义的渲染帧)应运而生。根据官方的定义,FMP 是指从页面加载开始,到大部分或者主要内容已经在首屏上渲染的时间点。
那么 FMP 时间点是怎么确认的呢,我们先看下最基本的计算方法:
我们首先计算布局对象(layout objects)数量(使用 LayoutAnalyzer 测试计算,详见资料17)。
根据下图可以看出,页面加载的过程其实就是布局对象逐步进入 layout tree 并进行渲染的过程。
layoutAnalyzer 会收集 layout objects 数量,有一个计数器叫做
LayoutObjectsThatHadNeverHadLayout,即首次新增的 layout objects 的个数。
通过测试发现相比于其他计数器,它变化最大的时刻,往往就是页面最重要的元素渲染了的时刻。
因此 FMP 指标的计算方法为:LayoutObjectsThatHadNeverHadLayout(新增的布局对象)发生变化最大的下一个时刻(paint that follows biggest layout change)。
当然,也存在一些场景不适用上述的情况:
a)、如果页面为长页面,那么会存在不可见布局对象增加的个数比首屏内可见对象增加个数更多的情况,此时 FMP 就是不准确的
b)、有加载web字体的情况,文字会使用降级字体进行布局,但是默认在 loadstart 开始的3s内,不进行绘制,这样也会影响FMP的计算
针对场景1,FMP 通过引入了 layout signifcance(布局重要度)的概念来解决该问题;针对场景2,FMP 通过推迟统计的时间,来让指标更加合理反映页面的情况。更加详细的解决方案可以参考资料18。
google也针对上述 FMP 的不同场景对近200个页面做了试验,通过人工看页面截图,与用户感受到的 FMP 准确度做对比,结果如下:
不过最终 FMP 在6.0的时候被废弃,主要是因为以下两点:
在生产环境中,FMP 对页面细微的变化太过敏感,容易导致结果不一致。
该指标的定义比较依赖于浏览器具体的实现细节,不具有可参考的标准性。
2. 代替 FMP 的 LCP 来了
上一个小章节中提到的了 FCP、FMP 的不足,因此W3C的性能小组也一直在想找一个合适的指标,更加真实反映用户看到页面主要内容的时间。
有时候简单点也许会更好(Sometimes simpler is better),根据多方关于页面性能的讨论,终于找到了一个更加准确衡量页面主要内容是否加载的方法,那就是 LCP(Largest Contentful Paint)。
LCP 指的是在视窗内,最大的内容元素被渲染的时间。这个指标在 Lighthouse 6.0中也正式加入,并且在最终性能评分中,有高达25%的权重。
LCP 应该是除了FCP以外最容易定义的指标,从定义可以看出,关键点就2个,选取哪些元素进行比较和如何确定元素的大小。
根据官方文档,下列元素会被纳入Largest Contentful 元素的考虑范围:
通过 url() 函数加载背景图片的元素
包含 text node 的块级元素或者 inline text 的子元素
那我们如何确定元素的大小?主要是以下 4 个规则:
在 viewport 内可见元素的大小,如果是超出可视区域或者被裁减、遮挡等,都不算入该元素大小
对于图片元素来说,大小是取图片实际大小和原始大小的较小值,即Min(实际大小,原始大小)
对于文字元素,只取能够覆盖文字的最小矩形面积
对所有元素,margin、padding、border 等都不算
google对该指标的评价如下:LCP 是一个十分重要并且以用户感受为中心的指标;它反映了感知层面上页面的加载速度;它标记了页面主要内容中最大内容元素加载完成的时间点;LCP 较短的页面能够让用户更快感觉到页面是可用的。
3. 被废弃的 FCI 是什么,为什么和 TTI 联系这么紧密
FCI(First Cpu Idle:首次CPU空闲),这个指标用来衡量一个页面需要多久才能达到 minimally interactive(最低限度交互)的标准。
而最低可交互的确认需要同时满足以下两个条件:
a) 大部分在屏幕上的 UI元素都是可交互的
b) 页面对大部分用户的输入响应,平均时间在一个合理的范围内
TTI(Time To Interactive:页面可交互时间),指的是页面达到完全可交互状态所需要的时间。
完全可交互指的是同时满足下面三个条件:
a) FCP 之后,页面已经呈现了有用的内容
b) 对大部分的可见页面元素而言,已经注册了事件回调
c) 页面对用户交互的响应在50ms以内
2017年,First Interactive 指标被分成了 First Interactive 和 Consistently Interactive 两个指标;第二年7月,First Interactive指标改名为 FCI,同时 Consistently Interactive 改名为 TTI 。可见,FCI 和 TTI 这两个指标是一对反映用户交互响应的指标。
那么最低可交互和完全可交互是怎么计算的呢?在介绍具体的计算方法之前,我们需要知道这两个指标都是模糊的并且在不同的情况下可以持续被优化改进的。
FCI 的最低限度可交互时间
在主线程的时间线中,从 FMP 开始且某个任务结束后,寻找到长度为 f(t) 的时间窗口 W。如果 W 满足在其任意时间段内,没有长度大于250ms的连续任务集,且前后1s内都没有长任务(js执行时间超过50ms的任务为长任务),那么该任务结束的时刻就是我们定义的 FCI 。其中 f(t) = 4 * e^(-0.045 * t) + 1。
下图红框中所指的时间点即为 FCI
具体的推导过程可以参考资料11,可以更深刻的理解到为什么 FCI 是模糊的一个概念。
TTI 的完全可交互时间
从网络和主线程的时间线中,找到第一个5s的窗口期 W,在 W 时间段满足:任意时刻没有超过两个同时进行中的网络请求、没有超过50ms的长任务。则W前的最后一个长任务结束时刻,就是我们说的 TTI。
下图红框中所指的时间点即为 TTI:
尽管有些人指出,FCI 在某些时候比 TTI 更有意义,但是它们之间的差异还是不足以让 Lighthouse 保留两个相似的指标。
因此在 Lighthouse 6.0里,最终选择还是使用 TTI 来取代 FCI。
4. mpFID 与新增的 TBT 指标
mpFID(max potential First Input Delay)指的是从用户输入到页面真正开始处理事件回调的潜在(可能)最大延迟时间。
mpFID 具体的计算方法,是从以 FCP 为开始到以 TTI 为结束的这段时间里,选择其中js执行时间最长的任务,用它消耗的时间再减去50ms。
但是 mpFID 表示的只是一个最大延迟时间,与用户实际输入的延迟时间是有差距的,用户在不同时刻的输入得到的 FID 也会不同,因此 mpFID 并不能真实反映页面对用户输入的响应时间。
在5.X版本计算性能分数的时候,mpFID 权重为0,并没有参与评分。虽然这个指标不再显示在报告中,但其实在JSON数据中还有保留,而 mpFID 也是官方依然认可的一个关键用户体验指标。
那究竟什么是 TBT(Total Blocking Time),为什么要在性能报告中选择用它代替 FID 呢?
先看下定义:TBT 指的是页面响应用户输入时,已经被阻塞的时间总和。
具体的计算方法比较明确,统计从 FCP 到 TTI 之间的所有长任务,并将它们的阻塞部分时间进行求和,即为 TBT。其中阻塞部分时间指的是长任务执行时间超过50ms的部分,例如一个长任务执行了70ms,那么阻塞时间就是20ms。
可以看出,TBT 相比于 mpFID 是一个更加稳定的指标,相较而言能更加真实地反映页面对于用户输入的响应平均延时。
5. 新增的CLS
CLS (Cumulative Layout Shift:累积布局变化),它是用来衡量视觉界面稳定性的一个指标。
这个数据的获取是由 Layout Instability API(详见参考资料14)提供的,计算方法如下:
layout shift score = impact fraction * distance fraction
其中 impact fraction 指的是对整个视窗的多少造成了影响。例如下图中的文字占整个视窗的50%,并且下一帧比上一帧向下移动了25%,因此对整个页面的75%造成了影响,因此 impact fraction 为0.75。
distance fraction 比较好理解,就是发生变化距离占整个视窗的比例,比如上面的例子,移动25%即 distance fraction 为0.25。
综上,图示demo的 CLS 值即为0.75 * 0.25 = 0.1875。更详细的计算方法可以参考资料13、14。
举一个 CLS 实际影响用户体验的例子:如下图所示,用户想点取消按钮,结果页面突然发生了布局变化,确认按钮出现在了之前取消的位置…
可见,CLS 是更从用户体验出发,而新增的一个性能评判指标。
目前 CLS 作为新晋指标所占权重还不大,仅为5%,但是 Lighthouse 已经在考虑下一个大版本中增加其权重了。
6. 一直都在的 Speed Index
速度指数 Speed Index(SI)是用来衡量页面可见内容填充快慢的指标,计算过程使用是开源工具 speedline(资料16)。
speedline 通过对页面进行视频录制,并统计首帧与最后一帧变化的时间差来计算 speed Index 的值。
值得一提的是 SI 的最终分数,会通过和数据库中真实网站的 SI 进行比较算出。目前 SI 分数与得分标准如下表所示:
四、总结与展望
回顾上述指标的更替过程我们可以发现,不论是从 FMP 到 LCP、FCI 到 TTI 还是 FID 到 TBT,目前在性能指标的选取上,都已经在朝着更加稳定的方向前进:指标的定义越来越简明清晰,并且计算的方式也趋于标准化。
但是我们也应该知道这里没有银弹,每个指标都会有其局限性。在很多场景下,并不能认为得分低,就一定代表着页面的体验差。而我们也只有了解了这些指标的背后原理,才能更加科学地结合性能得分对页面做出评价。
由于性能相关的技术更新速度迭代较快,如果文中有疏漏的地方,欢迎交流指正。
Node 社群
我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。
如果你觉得这篇内容对你有帮助,我想请你帮我2个小忙:
1. 点个「在看」,让更多人也能看到这篇文章 2. 订阅官方博客 www.inode.club 让我们一起成长 点赞和在看就是最大的支持❤️
五、参考资料
Find out how you stack up to new industry benchmarks for mobile page speed
Performance.timing Api
What's New in Lighthouse 6.0
Web Vitals
Lighthouse Scoring Calculator
Lighthouse performance scoring
WebPageTest Demo
Time to First Meaningful Paint
First Interactive and Consistently Interactive
Largest Contentful Paint (LCP)
First Interactive and Consistently Interactive
How Mercado Libre optimized for Web Vitals (TBT/FID)
Cumulative Layout Shift (CLS)
layout-instability
Speed Index
speedline
Layout Analyzer
Time to First Meaningful Paint