你还傻傻分不清楚 FP、FCP、LCP、DCL(DOMContentLoaded) 、L(Load) 出现的时机吗?

4,117 阅读6分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

本人最近在做一个前端性能监控的项目,需要使用FP、FCP、LCP、DCL(DOMContentLoaded) 、L(Load)这些指标,但是这个指标云里雾里,于是我决定彻底搞懂这些概念,并整理成此文,希望能帮助到你。

DCL 与 Load 的先后顺序

在我以往的印象中认为DCL(DOMContentLoaded)事件会比L(Load)提前调用的,但真的如此吗?

MDN 上的介绍如下:

DOMContentLoaded: 当初始的 HTML 文档被完全加载和解析完成之后,**DOMContentLoaded **事件被触发,而无需等待样式表、图像和子框架的完全加载。

load: load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。它与 DOMContentLoaded不同,后者只要页面 DOM 加载完成就触发,无需等待依赖资源的加载。

仔细读下上面的描述就知道,load事件强调的是从网络上加载数据,并不包括解析这些数据,而DOMContentLoaded是指加载完 HTML 数据(不包括样式、JS脚本等文件)并解析完才会触发。

那么,这两个事件谁先谁后触发就不一定了,这就有点意思了,那我们做个实验吧。

用浏览器打开下面的html文件,然后使用performance工具看下这两个事件的先后顺序。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>test</title>
  </head>
  <body>
    <div>test time</div>
  </body>
</html>

image.png

发现 load 事件比 DOMContentLoaded先触发,那如果我们加载一个图片呢?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>test</title>
  </head>
  <body>
    <div>test time</div>
    <img src="./yijiang-monitor/dist//people.png" alt="" />
  </body>
</html>

image.png

发现 load 事件比 DOMContentLoaded后触发。

所以,我们明白了如果页面有很多外部资源需要加载,那么load 事件会后触发,如果页面内容较多,外部资源较少,那么load事件先触发。

FP、FCP、LCP

最初,评价前端页面加载性能有两个指标:DOMContentLoadedload事件,分别代表 DOM 创建完成和首屏静态资源加载完成。

对于之前的页面和现代的服务端渲染的页面,这两个指标都可以很好地衡量首屏内容展示时间。但对于现代复杂的单页应用,都是通过 JS 操作 DOM 向页面添加主要内容,对于这种场景,DOMContentLoadedload 事件就不能很好地衡量首屏显示时间了。

于是有FP、FCP、FMP被提出来,它们关注的不是"加载",而是"渲染",因此能更好地表现用户看到的情况。

FP、FCP 这两个指标虽然表达了渲染的事件,但对"用户关注的内容"没有体现,比如首屏渲染出来一个背景,或者一个loading,可能对于用户来说和白屏区别不大。

FMP虽然体现了"关键内容"的要素,但它是复杂的、模糊的,甚至是错误的,并不能准确识别页面主要内容的加载时机。

后来LCP指标被提出来,表示"用于度量视口中最大的内容元素何时可见",它用来代替FMP,表征页面的关键元素何时可以被用户看到。

我们知道,浏览器访问页面时候,浏览器将页面从网络下载到本地后,主要做几个事情:解析HTML,创建DOM,同时加载依赖的资源:CSS、图片等(加载资源的过程不会阻塞DOM解析),然后调用渲染进程渲染到界面上。

这里需要注意一点,在现在浏览器中,为了减缓渲染被阻塞的情况,现代的浏览器都使用了猜测预加载。当解析被阻塞的时候,浏览器会有一个轻量级的HTML(或CSS)扫描器(scanner)继续在文档中扫描,查找那些将来可能能够用到的资源文件的url,在渲染器使用它们之前将其下载下来。

在整个加载和渲染过程中会触发多个事件:

  • Load(Onload Event),它代表页面中依赖的所有资源加载完的事件。

  • DCL(DOMContentLoaded),DOM解析完毕。

  • FP(First Paint),表示渲染出第一个像素点。FP一般在HTML解析完成或者解析一部分时候触发

  • FCP(First Contentful Paint),表示渲染出第一个内容,这里的"内容"可以是文本、图片、canvas。

  • FMP(First Meaningful Paint),首次渲染有意义的内容的时间,"有意义"没有一个标准的定义,FMP的计算方法也很复杂。

  • LCP(largest contentful Paint),最大内容渲染时间。

上面有一句是: FP一般在HTML解析完成或者解析一部分时候触发,那么来看看这两个事件执行的先后是怎么样的?

FP 与 DCL 的先后顺序

浏览器不一定等到所有的DOM都解析完再开始渲染,如果DOM节点少,浏览器会加载完再渲染,但是如果节点很多,浏览器解析一部分节点后就会开始渲染(这时候就会触发FP)。也就是说,当需要渲染的节点数少的时候,DCL会在FP前面;当需要渲染的节点数很多时候,DCL会在FP后面。

当节点较少的时候,performance面板中可以发现 FPDCL之后触发。

image.png

当节点较多时,我们可以打开百度官网来查看下performance面板,因为页面DOM节点较多,所以先触发FP

image.png

现在来说,绝大部分的项目都是FPDCL之前触发,这样用户可以更快的看到页面内容。

FP 和FCP 的关系

浏览器渲染的界面可能是"内容",例如文本,也可能不是"内容",比如一个背景为红色的div标签。FCP事件指渲染出第一个内容的事件,而FP指渲染出第一个像素点,渲染出的东西可能是内容,也可能不是。

有节点不一定有渲染,如果没有任何样式,是没有界面的,也不需要渲染。下面代码就没有 FP 事件。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>no FP</title>
  </head>
  <body>
    <div></div>
  </body>
</html>

image.png

下面代码,会渲染界面,因此会触发FP事件,但是不会触发FCP,因为没有内容:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>has FP, no FCP</title>
    <style>
        div {
            width: 1px;
            height: 1px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div></div>
</body>
</html>

image.png

通过上面对FP和FCP的介绍,可以知道,如果html本身有内容(文本、图片)或者js脚本很快能创建内容,那么FP和FCP会一起触发。否则FP比FCP提前触发。FP肯定不会在FCP后面出现,因为渲染出内容,一定也渲染出了像素。

可以用一张图来表示各个事件的时间节点:

image.png

文档首次可交互时间可以用来衡量页面可交互的性能。

首次可交互,即DOM加载并解析完成后,界面上的元素可以交互(如输入框可以输入、按钮可以点击、超长元素可以滚动)。其时间用performance.timing.domInteractive计算。