daily-plan icon indicating copy to clipboard operation
daily-plan copied to clipboard

精读《Time to First Meaningful Paint: a layout-based approach》

Open sl1673495 opened this issue 4 years ago • 0 comments

为什么需要监控?

web 的性能一定程度上影响了用户留存率,Google DoubleClick 研究表明:如果一个移动端页面加载时长超过 3 秒,用户就会放弃而离开。BBC 发现网页加载时长每增加 1 秒,用户就会流失 10%。

监控什么?

google 开发者提出了一种 RAIL 模型来衡量应用性能,即:Response、Animation、Idle、Load,分别代表着 web 应用生命周期的四个不同方面。并指出最好的性能指标是:100ms 内响应用户输入;动画或者滚动需在 10ms 内产生下一帧;最大化空闲时间;页面加载时长不超过 5 秒。

first paint (FP) and first contentful paint (FCP)

首次渲染、首次有内容的渲染。

window.performance.getEntriesByType('paint')

FCP 一般是 some contentful thing (text, image, canvas, or SVG) 渲染的时机,但不保证是有意义的。

First meaningful paint(FMP) and hero element timing

首次有意义的渲染、页面关键元素

这个网页主要内容出现的时候,那么在这样的一个时间点上,就是首次有意义的渲染。但是这个关键点很难定义。

这里有一篇文章 Time to First Meaningful Paint: a layout-based approach 整理一下文中的观点:

精读 Time to First Meaningful Paint

基本方法

First meaningful paint = Paint that follows biggest layout change

也就是 layout objects 页面元素的数量突然剧增最多的那个时间点,下一个时间点就是 FMP。

启发式算法

集成长页面

但是上面这种方式有一个问题,就是如果页面有一些不可见的元素数量剧增的话,会错误的把那个时间点作为 FMP。

新的启发式公式:

layout significance = number of layout objects added / max(1, page height / screen height)

我们或许第一反应是计算元素是否可见,但是应该避免这种昂贵的计算,所以「启发式算法」是:

元素增加的数量 / (整个页面的高度和屏幕高度的比例),这样页面尾部的元素虽然剧增,但是假如此时页面的高度已经是屏幕高度的 4 倍了,那就要把这个数量除以 4,自然就比不过首屏元素剧增的那个时间点了。

字体

有时候虽然内容渲染了很多,但是字体还没加载完成,字体加载的第一阶段是 font block period,此时如果字体还没有加载完成,会 render with an invisible fallback font face 来替代,注意这个 invisible,它创建了一个匿名的 font-face 和实际字体使用相同指标,但是所有字形都“不可见”。此时不能说这个内容是有意义的。

所以,FMP 必须考虑加载字体的数量

因此,我们引入一种启发式方法:如果在布局发生时有加载字体,则布局更改计算将推迟到显示字体为止(如果超过 3 秒超时,则使用加载字体或后备字体)。

注意事项

  1. 这个测量仅基于新出现的布局对象,不考虑现有布局对象的大小/位置变化。

  2. 对于某些页面,图像对于首先有意义的绘画至关重要。 可能我们需要一些待处理图像的试探法,类似于待处理的 Web 字体。

  3. 由于此指标基于布局对象,因此不受未附加到布局树的 DOM 元素的影响(例如 display:none)。 但是,某些页面会在覆盖元素下或透明层上呈现内容,以提供“淡入”加载体验。 对于这些情况,我们的方法检测到用户看不到的版式更改,并报告无意义的时间。

Time to interactive

可交互时间 image

长任务

image 浏览器是单线程的,如果长任务过多,那必然会影响着用户响应时长

监测长任务: image

Navigation Timing API

performance.getEntriesByType("navigation");

  • 重定向次数:performance.navigation.redirectCount
  • 重定向耗时: redirectEnd - redirectStart
  • DNS 解析耗时: domainLookupEnd - domainLookupStart
  • TCP 连接耗时: connectEnd - connectStart
  • SSL 安全连接耗时: connectEnd - secureConnectionStart
  • 网络请求耗时 (TTFB): responseStart - requestStart
  • 数据传输耗时: responseEnd - responseStart
  • DOM 解析耗时: domInteractive - responseEnd
  • 资源加载耗时: loadEventStart - domContentLoadedEventEnd
  • 首包时间: responseStart - domainLookupStart
  • 白屏时间: responseEnd - fetchStart
  • 首次可交互时间: domInteractive - fetchStart
  • DOM Ready 时间: domContentLoadEventEnd - fetchStart
  • 页面完全加载时间: loadEventStart - fetchStart
  • http 头部大小:transferSize - encodedBodySize

image

Resource Timing API

image

image

// 某类资源的加载时间,可测量图片、js、css、XHR
resourceListEntries.forEach(resource => {
    if (resource.initiatorType == 'img') {
    console.info(`Time taken to load ${resource.name}: `, resource.responseEnd - resource.startTime);
    }
});

User Timing API

https://www.w3.org/TR/user-timing-2/#introduction

主要是利用 mark 和 measure 方法去打点计算某个阶段的耗时,例如某个函数的耗时等。

High Resolution Time API

https://w3c.github.io/hr-time/#dom-performance-timeorigin

主要包括 now() 方法和 timeOrigin 属性。

Performance Timeline API

https://www.w3.org/TR/performance-timeline-2/#introduction

google 开发者推荐的上报方式:

尽可能的选用 Navigator.sendBeacon() 去上报,它可以在文档卸载期间发送数据。用 sendBeacon() 方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。此外,代码实际上还要比其他技术简单许多!

image

sl1673495 avatar May 19 '20 08:05 sl1673495