共计 4768 个字符,预计需要花费 12 分钟才能阅读完成。
一些废话
总所周知,写文章须要一个题目。尽管咱们搞代码的人个别都喜爱单刀直入,然而受制于文体的束缚和发表载体的要求,有时不得不想一个题目。而起一个题目,不亚于起一个函数名或者变量名。单就这篇文章,我就有好几个草稿题目,例如:《页面加载指标演进之路》,《Element Timing:一种全新的页面速度指标》,《如何最精确地测量网页加载速度》,《新前端下的页面加载速度》,甚至《Element Timing In Action》,《三分钟学会测量页面速度》。最初综合思考了读者的承受能力,编辑的意见,以及最最重要的:自己的孱弱写作实力,就取了个这样的一个十分大众化,既不会一眼就被当成垃圾,也不会被人挑出来认真找茬的题目。
好了,废话不能多说,间接进入正题。
DOMReady
上古期间(指距今 10+ 年前的 jQuery 纪元),咱们开发网页还停留在编写动态页面构造,用 JS 脚本对 DOM 进行间接操作,增加删除一些额定页面元素上。此时,DOMReady 根本就能够满足计算页面加载实现工夫的需要,DOMReady(在 DOM 事件中是 DOMContentLoaded)代表页面文档齐全加载并解析结束, 个别蕴含 HTML 文档剖析以及 DOM 树的创立、外链脚本的加载、外链脚本的执行以及内联脚本的执行,而不会期待款式文件, 图片文件, 子框架页面的加载。个别在页面 header 中打个工夫戳,再在 jQuery 的 domReady 事件中打个工夫戳,咱们就能够计算到大抵的 DOMReady 耗时了。
Navigation Timing
中古期间(指距今 10-5 年左右的 Ajax 纪元),网页的交互模式更加丰盛多样,Gmail 为首的富网页利用在用户体验大幅加强的同时,也给细粒度的网页加载工夫记录带来了需要。因而,从 2010 年开始 Web 性能工作组就曾经为 Web 引入了大量工夫信息记录,能够通过 window 对象的 performance 属性去获取。这就是起初咱们所熟知的 Navigation Timing。
Navigation Timing 接口所提供的数据大抵如图:
基本上囊括了从网页开始网络申请到页面残缺加载并执行完资源并实现初始化 DOM 节点的工夫。咱们间接应用 performance.timing,就能够轻松取得这些工夫来帮忙剖析页面的加载工夫。
FP, FCP 和 LCP
近古期间(指距今 5-2 年左右的 React 纪元),因为各种前端框架(React,Vue 等)雨后春笋似的涌出,加上 Webpack 这种前端构建神器的呈现,导致 Web 页面的开发难度迅速降落,复杂度也直线回升。重前端的利用大行其道,页面加载脚本的工夫也迅速变长,很多网站为了体验采取了渐进式加载的策略,以解决期待脚本执行时白屏工夫过长的问题。因而,渐进式网页渲染指标也应运而生。
渐进式网页指标个别有这几个:
- 首次绘制(FP):全称 First Paint,标记浏览器渲染任何在视觉上不同于导航前屏幕内容之内容的工夫点
- 首次内容绘制(FCP):全称 First Contentful Paint,标记的是浏览器渲染来自 DOM 第一位内容的工夫点,该内容可能是文本、图像、SVG 甚至 <canvas> 元素。
- 首次无效绘制(FMP):全称 First Meaningful Paint,标记的是页面次要内容绘制的工夫点,例如视频利用的视频组件、天气利用的天气信息、新闻利用中的新闻条目。
- 最大内容绘制(LCP):全称 Largest Contentful Paint,标记在可视区“内容”最大的可见元素开始绘制在屏幕上的工夫点。
其中 FMP 因为依赖算法猜想无效元素,所以目前曾经根本被弃用。这几个指标的可视化意义能够参考以下两张图:
因为简单页面的元素往往很多,FCP 所观测的元素可能只是 举足轻重 的一个 Loading 标记或者一个边栏,因而对于实在的用户来说,并不能代表页面的“首屏工夫”。反而,在某些逻辑简单的页面中,因为 JS 代码的执行工夫长,或者依赖很多后端接口来渲染页面,常常会导致页面最重要的数据展现的工夫远远长于页面 OnLoadEvent 触发的工夫,此时,对于用户来说最直观感觉的到的“首屏工夫”,往往就是 LCP 的工夫。
这就是当初很多前端页面性能工具都会把 LCP 列入一个重要的参考指标的起因。
Element Timing
古代期间(指距今 1-0 年左右的微前端纪元),LCP 的计算逻辑是浏览器给定的,在不同页面中,浏览器所认为的 最大的可见元素 也未必是咱们业务中 真正重要的 内容。并且在微前端风行的古代,不仅仅是同一利用的不同页面采纳单页模式,甚至不同子利用的加载也可能通过 hash 路由来驱动。对于这种单页利用来说,以上的各个指标其实都无奈满足在主体框架加载实现后切换不同页面时的从新计算。那么咱们是不是只可能齐全依赖业务开发自身去在代码里被动打点和上报加载工夫呢?那也未必。
让咱们来看看 W3C 的一个新草案,元素计时 API:https://wicg.github.io/element-timing/。只管这个 API 还处于草案阶段,然而 Chrome 和 Edge 两个浏览器其实早已在新版本给予了反对:兼容性
Element Timing API 的目标是让 Web 开发人员或剖析工具可能测量页面上要害元素的渲染工夫,比起 LCP,咱们可能本人来定义要害元素,这正是 Element Timing 的最大魅力。
Element Timing 反对的元素有:
<img>
元素<svg>
中的<img>
元素<video>
中的 poster image- 领有
background-image
的元素 - 一组文本节点
举个例子:
<img src="image.jpg" elementtiming="big-image">
<p elementtiming="text" id="text-id">text here</p>
const observer = new PerformanceObserver((list) => {let entries = list.getEntries().forEach(function (entry) {console.log(entry);
});
});
observer.observe({entryTypes: ["element"] });
// 输入 entry 内容:// {
// duration: 0
// element: p.aimake-site-name
// entryType: "element"
// id: ""// identifier:"text-id"
// intersectionRect: DOMRectReadOnly {x: 236, y: 130, width: 144, height: 28, top: 130, …}
// loadTime: 0
// name: "text-paint"
// naturalHeight: 0
// naturalWidth: 0
// renderTime: 10850.899999976158
// startTime: 10850.899999976158
// url: ""
// }
然而须要留神的是,并不是所有文本节点都能够通过增加 elementtiming=""
让 Element Timing API 辨认,WICG 的解释中有一段注意事项:
We say that a text node belongs to the closest block-level Element ancestor of the node. This means that an element could have 0 or many associated text nodes with it.
We say that an element is text-painted if at least one text node belongs to and has been painted at least once. Thus, the text rendering timestamp of an element is the time when it becomes _text-painted_.
Let the text rect of a text node be the display rectangle of that node within the viewport. We define the text rect of an element as the smallest rectangle which contains the geometric union of the text rects of all text nodes which belong to the element.
读起来比拟难懂,然而实际上它想阐明的是,只有满足以下条件的文本节点才可能被记录:
- 必须是块级元素:如
<p>
,<h1>
,<div>
,<section>
等,也就是说,独自的<span>
元素等行内元素,即时增加了elementtiming=""
属性也并不会被记录。 - 间接子节点必须蕴含一个或多个文本节点:例如 纯文本,
<span>
,<i>
,<b>
等,<p>
等块级元素则不算,<img>
这种图像也不算。
举一些例子就懂了:
<html>
<p elementtiming = "p1"><p>1</p></p> <!-- 有效 -->
<p elementtiming = "p2">2</p> <!-- 无效 -->
<span elementtiming = "span1">span1</span> <!-- 有效 -->
<div elementtiming = "div1"><image elementtiming = "img1" src="https://img.alicdn.com/tfs/xxx.png"></image></div> <!-- div1 有效,其中的 img1 无效 -->
<div elementtiming = "div2"><span>1</span></div> <!-- 无效 -->
<div elementtiming = "div3"><p>2</p></div> <!-- 有效 -->
<div elementtiming = "div4"><h1>3</h1></div> <!-- 有效 -->
<div elementtiming = "div5"><b>4</b></div> <!-- 无效 -->
<b elementtiming = "b1">b1</b> <!-- 有效 -->
<i elementtiming = "i1">i1</i> <!-- 有效 -->
<h1 elementtiming = "h1">h1</h1> <!-- 无效 -->
<section elementtiming = "section1">section1</section> <!-- 无效 -->
</html>
在增加了自定义 elementtiming 属性后,当所标记的图像或者文本节点被 真正渲染 时,浏览器就会记录下工夫。因而,咱们能够在不同利用中让开发同学间接给可能标记 首屏 的元素增加该属性,即可由采集脚本通过监听 PerformanceObserver
来对立采集到元素绘制的工夫点(renderTime)了。
通过应用 Element Timing API,咱们可能更准确记录到每个利用,页面,甚至功能模块的加载时长。这才是最古代,最前沿的页面加载工夫计划,其余计划最终都将被掩埋在历史的尘埃中!开个玩笑
另一些废话
江山代有才人出,各领风骚数百年
一般来说,结尾并没有题目那么重要,因而我也不须要费脑子去想 N 个结尾了,间接简略总结一下:无论前端倒退到什么水平,Timing 的规范总会追上你!
心愿如果有读者大神如果受到了一点点启发,可能给写个蕴含 Element Timing 指标的性能库造福咱们。毕竟我就是懒,以上都是我本人 YY 的用法。在此先提前感激了。
作者:ES2049 | 佚名
文章可随便转载,但请保留此原文链接。
十分欢送有激情的你退出 ES2049 Studio,简历请发送至 caijun.hcj@alibaba-inc.com。