前端性能定位|原理篇
30 秒速记
- 核心判断:性能优化先建立用户指标与资源、主线程、渲染和网络证据,再定位最长关键路径
- 原理主线:围绕 「性能优化的意义」、「RAIL模型」、「常见性能指标」 建立输入、状态变化与输出之间的因果关系
- 文章范围:系统介绍了前端性能优化的重要性、用户体验标准、RAIL性能模型及常见性能指标,帮助开发者高效定位和提升Web页面性能,提升用户留存和转化率。
- 边界与代价:实验室分数不等于真实用户体验,平均值也会掩盖长尾;优化一项指标可能转移成本
- 工程落地:用现场数据确定瓶颈、做最小改动、对照验证并持续监控回归
前端性能定位不能只看“页面慢不慢”,要结合用户感知指标,找到网络、主线程或渲染链路中真正的瓶颈。 我一般先看 FCP、LCP、FID、CLS,判断问题属于内容出现慢、交互响应慢还是布局不稳定。再通过资源耗时、长任务和帧率缩小范围,例如 TBT 偏高通常说明主线程被超过 50ms 的任务持续占用。指标定义和阈值可能受工具及版本影响,落到项目时还要结合实际设备、网络条件和可复现现象验证。
这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。
版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。
# 性能优化的意义
# 性能是留住用户很重要的一环
- pinterest 重建了他们的页面以实现性能,使感知等待时间减少了 40%,从而将搜索引擎流量和注册量提高了 15%。
- 通过将平均页面加载时间减少 850 毫秒,COOK 发现他们能够将转化率提高 7%,将跳出率降低 7%,并将每个页面的页面数量增加 10%。
- BBC 发现,他们在网站加载的每一秒钟内就会损失 10% 的用户。
- DoubleClick by Google 发现,如果网页加载时间超过 3 秒,则会有 53% 的用户放弃移动网站的访问。
# 性能是改善转换率至关重要的一环
- 对于 Mobify,主页加载速度每减少 100 毫秒,基于会话的转换增加 1.11%,平均年收入增加近 380,000 美元。此外,结账页面加载速度减少 100 毫秒,基于会话的转换增加了 1.55%,从而使年均收入增长近 530,000 美元
- DoubleClick 发现在 5 秒内加载网站的发布商的广告收入是在 19 秒内加载的网站的两倍。. 当 AutoAnything 将页面加载时间缩短一半时,他们的销售额增长了 12-13%。
# 用户角度的性能标准是什么
# 著名的 2-5-8 原则
- 当用户能够在 2 秒以内得到响应时,会感觉系统的响应很快;
- 当用户在 2-5 秒之间得到响应时,会感觉系统的响应速度还可以;
- 当用户在 5-8 秒以内得到响应时,会感觉系统的响应速度很慢,但是还可以接受;
- 而当用户在超过 8 秒后仍然无法得到响应时,会感觉系统糟透了,或者认为系统已经失去响应,而选择离开这个 Web 站点,或者发起第二次请求。
综上所述:一个网站的性能好坏是留住用户和实现变现的基础
而我们的目标就是力争 1s,保住 2s
1s 的差距,看似微乎其微,但这 1s,浏览器实际上可以做非常多的事情 接下来让我们来看看如何对一个网站进行性能分析
# RAIL模型
RAIL是Response,Animation,Idle和Load的首字母缩写,是一种由Google Chrome团队于2015年提出的性能模型,用于提升浏览器的用户体验和性能。
RAIL模型的理念是以用户为中心,最终目标并不是让你的网站在任何特定设备上都能运行很快,而是使用户满意。
Response: 应该尽可能快速的响应用户的操作,应在在100ms以内响应用户输入。
Animation: 在展示动画的时候,每一帧应该以16ms进行渲染,这样可以保持动画效果的一致性,并且避免卡顿。
Idle: 当使用js主线程的时候,应该把任务划分到执行时间小于50ms的片段中去,这样可以释放线程以进行用户交互。50ms为单位是为了保证用户在发生操作的100ms内做出响应。
要使网站响应迅速,动画流畅,通常都需要较长的处理时间,但以用户为中心来看待性能问题,就会发现并非所有工作都需要在响应和加载阶段完成,完全可以利用浏览器的空闲时间处理可延迟的任务,只要让用户感受不到延迟即可。利用空闲时间处理延迟可减少预加载的数据大小,以保证网站或应用快速完成加载。
Load: 应该在小于1s的时间内加载完成你的网站,并可以进行用户交互。根据网络条件和硬件的不同,用户对性能延迟的理解也有所不同,在3G网络需要花费更多的时间,5s是一个更现实的目标。
# 常见性能指标
# 1. FP 白屏(First Paint )
首次绘制,是时间线上的第一个时间点,它代表网页的第一个像素渲染到屏幕上所用时间,也就是页面在屏幕上首次发生视觉变化的时间
白屏时间过长,会让用户认为我们的页面不能用或者可用性差

统计逻辑
通过performance.getEntriesByType('paint'),取第一个paint的时间。如:
function getFPTime(){
const timings = performance.getEntriesByType('paint')[0];
return timings ? Math.round(timings.startTime) : null
}
# 2.FCP 首屏(first contentful paint )
首次内容绘制,它代表浏览器第一次向屏幕绘内容。注意:只有首次绘制文本、图片(包含背景图)、非白色的canvas或SVG时才被算作FCP

| FCP时间(秒) | 颜色编码 | FPC分数 |
|---|---|---|
| 0 - 2 | 绿色(快) | 75 - 100 |
| 2 - 4 | 橙色(中等) | 50 - 74 |
| 超过4 | 红色(慢) | 0 - 49 |
统计逻辑
通过performance.getEntriesByType('paint’),取第二个paint的时间,或者通过Mutation Observer观察到首次节点变动的时间。如
function getFPTime(){
const timings = performance.getEntriesByType('paint');
if(timings.length > 1)return timings[1]
return timings ? Math.round(timings.startTime) : null
}
function getFPTime(){
const domEntries = []
const observer = new MutationObserver((mutationsList)=>{
for(var mutation of mutationsList) {
if (mutation.type == 'childList') {
console.log('A child node has been added or removed.');
}
if (mutation.type == 'addedNodes') {
//TODO新增了节点,做处理,计算此时的可见性/位置/出现时间等信息,然后 push 进数组
...
domEntries.push(mutation)
}
}
});
//伪代码,算 DOM 变化时的最小那个时间,即节点首次变动的时间
return Math.round(domEntries.length ? Math.min(...domEntries.map(entry => entry.time)) : 0);
}
注意
FP与FCP这两个指标之间的主要区别是:FP是当浏览器开始绘制内容到屏幕上的时候,只要在视觉上开始发生变化,无论是什么内容触发的视觉变化,在这一刻,这个时间点,叫做FP。- 相比之下,FCP指的是浏览器首次绘制来自DOM的内容。例如:文本,图片,SVG,canvas元素等,这个时间点叫
FCP。 FP和FCP可能是相同的时间,也可能是先FP后FCP。
# 3. FMP 首次有意义的绘制(First Meaningful Paint )
是页面主要内容出现在屏幕上的时间, 这是用户感知加载体验的主要指标。目前尚无标准化的定义, 因为很难以通用的方式去确定各种类型页面的关键内容(只是记录了加载体验的最开始。如果页面显示的是启动图片或者 loading 动画,这个时刻对用用户而言没有意义))
统计逻辑
- 目前没有统一逻辑,阿里有个标准为最高可见增量元素,采用深度优先遍历方法,详细可见
- 其次,可以自定义,比如通
Mutation Observer观察页面加载的一段时间(如前20s)内页面节点的变化, 即元素新增/移除/隐藏等变化记录下来,这样可以得到页面元素的可见时间点及元素与可视区域的交叉信息等。 - 然后,自定义一个计算公式,比如根据元素的类型进行权重取值(
div权重1,img权重2等), 然后取元素与可视区域的交叉区域面积、可见度、 权重值之间的乘积为元素评分。 - 根据上面得到的信息, 以时间点为X轴, 该时间点可见元素的评分总和为Y轴, 取最高点对应的最小时间为页面主要内容出现在屏幕上的时间
# 4.LCP(Largest Contentful Paint )
最大内容绘制,可视区域中最大的内容元素呈现到屏幕上的时间,用以估算页面的主要内容对用户的可见时间。
img图片,video元素的封面,通过url加载到的背景,文本节点等,为了提供更好的用户体验,网站应该在2.5s以内或者更短的时间最大内容绘制
| LCP时间(秒) | 颜色编码 |
|---|---|
| 0 - 2.5 | 绿色(快) |
| 2.5 - 4 | 橙色(中等) |
| 超过4 | 红色(慢) |
# 5. FID (first Input Delay)
首次输入延迟,是测量用户首次与您的站点交互时的时间(即当他们单击链接/点击按钮/使用自定义的JavaScript驱动控件时)到浏览器实际能够回应这种互动的时间
输入延迟是因为浏览器的主线程正忙于做其他事情,所以不能响应用户,发生这种情况的一个常见原因是浏览器正忙于解析和执行应用程序加载的大量计算的JavaScript
| FID时间(毫秒) | 颜色编码 |
|---|---|
| 0 - 100 | 绿色(快) |
| 100 - 300 | 橙色(中等) |
| 超过300 | 红色(慢) |

统计逻辑
- 方式一,通过
performanceObserver(目前支持性为88.78%)观察类型为first-input的entry,获得其startTime/duration等数即可 - 方式二,初始化时为特定事件类型(
click/touch/keydown)绑定通用统计逻辑事件,开始调用时从event.timeStamp取开始处理的时间(这个时间就是首次输入延迟时间),在事件处理中注册requestIdleCallback事件回调onIdleCallback,当onIdleCallback被执行时,当前时间减开始的event.timeStamp即为duration时间
