基于RUM的前端优化理论与实践-性能篇
共 7392字,需浏览 15分钟
·
2021-09-12 23:07
前言
对于前端来说,最重要的是体验,而在前端体验中,最为核心的就是性能。
前端页面性能关键指标的规范和计算规则。
如何看懂RUM可视化图表?并通过图表数据进行项目优化?
页面性能指标有哪些?
网络连接瀑布图(TL;DR)
与这张图一一对应的,是浏览器里面的`performance.timing`属性,我们将其同时打印出来,做一个数据的对比说明。
navigationStart:表示从上一个文档卸载结束时的unix时间戳,如果没有上一个文档,这个值将和fetchStart相等。
unloadEventStart:表示前一个网页(与当前页面同域)unload的时间戳,如果无前一个网页unload或者前一个网页与当前页面不同域,则值为0。
unloadEventEnd:返回前一个页面unload时间绑定的回调函数执行完毕的时间戳。
redirectStart:第一个HTTP重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为0。
redirectEnd:最后一个HTTP重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为0。
fetchStart:浏览器准备好使用HTTP请求抓取文档的时间,这发生在检查本地缓存之前。
domainLookupStart/domainLookupEnd:DNS域名查询开始/结束的时间,如果使用了本地缓存(即无DNS查询)或持久连接,则与fetchStart值相等。
connectStart:HTTP(TCP)开始/重新 建立连接的时间,如果是持久连接,则与fetchStart值相等。
connectEnd:HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与fetchStart值相等。
secureConnectionStart:HTTPS连接开始的时间,如果不是安全连接,则值为0。
requestStart:HTTP请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存。
responseStart:HTTP开始接收响应的时间(获取到第一个字节),包括从本地读取缓存。
responseEnd:HTTP响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存。
domLoading:开始解析渲染DOM树的时间,此时 Document.readyState变为loading,并将抛出readystatechange相关事件。
domInteractive:完成解析DOM树的时间,Document.readyState变为interactive,并将抛出readystatechange相关事件,注意只是DOM树解析完成,这时候并没有开始加载网页内的资源。
domContentLoadedEventStart:DOM解析完成后,网页内资源加载开始的时间,在DOMContentLoaded事件抛出前发生。
domContentLoadedEventEnd:DOM解析完成后,网页内资源加载完成的时间(如JS脚本加载执行完毕)。
domComplete::DOM树解析完成,且资源也准备就绪的时间,Document.readyState变为complete,并将抛出readystatechange相关事件。
loadEventStart:load事件发送给文档,也即load回调函数开始执行的时间。
loadEventEnd:load事件的回调函数执行完毕的时间。
// 计算加载时间
getPerformanceTiming() {
const t = performance.timing;
const times = {};
// 页面加载完成的时间,用户等待页面可用的时间
times.loadPage = t.loadEventEnd - t.navigationStart;
// 解析 DOM 树结构的时间
times.domReady = t.domComplete - t.responseEnd;
// 重定向的时间
times.redirect = t.redirectEnd - t.redirectStart;
// DNS 查询时间
times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
// 读取页面第一个字节的时间
times.ttfb = t.responseStart - t.navigationStart;
// 资源请求加载完成的时间
times.request = t.responseEnd - t.requestStart;
// 执行 onload 回调函数的时间
times.loadEvent = t.loadEventEnd - t.loadEventStart;
// DNS 缓存时间
times.appcache = t.domainLookupStart - t.fetchStart;
// 卸载页面的时间
times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;
// TCP 建立连接完成握手的时间
times.connect = t.connectEnd - t.connectStart;
return times;
}
RUM使用了哪些性能指标?
页面加载瀑布图
Aegis SDK源码中对其的计算规则如下:
const t: PerformanceTiming = performance.timing;
if (!t) return;
这里不知道为什么有时候 t.loadEventStart - t.domInteractive 返回一个很大的负数,暂时先简单处理
let resourceDownload = t.loadEventStart - t.domInteractive;
if (resourceDownload < 0) resourceDownload = 1070;
result = {
dnsLookup: t.domainLookupEnd - t.domainLookupStart,
tcp: t.connectEnd - t.connectStart,
ssl: t.secureConnectionStart === 0 ? 0 : t.requestStart - t.secureConnectionStart,
ttfb: t.responseStart - t.requestStart,
contentDownload: t.responseEnd - t.responseStart,
domParse: t.domInteractive - t.domLoading,
resourceDownload
};
默认通过MutationObserver这个API来监控浏览器document对象的DOM变化,只计算在首屏内的DOM元素,把DOM变化时间作为x轴,单位时间内DOM变化的数量作为y轴,绘制曲线后,我们找到DOM变化最高点,认为是首屏完成。
如果开发者觉得该算法不准确,希望自己标记DOM元素,可以添加属性<div AEGIS-FIRST-SCREEN-TIMING></div>,把某个元素识别为首屏关键元素,SDK认为只要用户首屏出现此元素就是首屏完成。也可以添加属性<div AEGIS-IGNORE-FIRST-SCREEN-TIMING></div>,把该DOM列入黑名单。
首字节(TTFB)=DNS+SSL+TCP+TTFB
DOMReady=DNS+SSL+TCP+TTFB+ContentDownload+DomParse
页面完全加载=DNS+SSL+TCP+TTFB+ContentDownload+ DomParse+ResourceDownload
良好网站的基本指标-Web Vitals
上述计算首屏的算法是Aegis SDK自主提供的算法,用户场景千变万化,无法覆盖所有场景,而且这个算法也无法得到所有开发者的认同。这个时候就需要祭出Web Vitals了。
什么是Web Vitals?
CLS(Cumulative Layout Shift-累积布局移位):CLS会衡量在网页的整个生命周期内发生的所有意外布局偏移的得分总和。得分是零到任意正数,其中0表示无偏移,且数字越大,网页的布局偏移越大。
FCP(First Contentful Paint-首次内容绘制):FCP度量从页面开始加载到页面内容的任何部分呈现在屏幕上的时间,页面内容包括文本、图像(包括背景图像)、<svg>元素或非白色的<canvas>元素。
FID(First Input Delay-首次输入延迟):从用户首次与您的网页互动(点击链接、点按按钮,等等)到浏览器响应此次互动之间的用时。这种衡量方案的对象是被用户首次点击的任何互动式元素。
LCP(Largest Contentful Paint-最大内容绘制):LCP度量从用户请求网址到在视口中渲染最大可见内容元素所需的时间。最大的元素通常是图片或视频,也可能是大型块级文本元素。
TTFB (Time To First Byte-从服务器接收到第一个字节耗时) :TTFB 是发出页面请求到接收到应答数据第一个字节的时间总和,它包含了DNS解析时间、TCP连接时间、发送HTTP请求时间和获得响应消息第一个字节的时间。
如何分析性能数据&指导开发优化?
拥有了RUM这个好用的工具,下面就可以用数据指导开发和决策了。
优化举例
资源加载优化
拆包,通过把公共外部依赖打包成为vendor,并且对组件做异步加载。
去掉一些非必需的包,比如用户引入了全量的lodash,让其改成lodash-es,方便webpack做treeshaking;去掉仅为了把某个时间做格式化而引入的moment;去掉jquery,而当初引入jquery仅仅为了查询某个元素而,真是得不偿失。
建议使用webpack-bundle-analyzer对打包后的代码进行分析,查看哪些包不需要引用,或者可以单独打包。
网络协议方面全面引入HTTP2,合并了一些小的静态资源,把一些小的svg改成了base64。
页面主进程阻塞严重,Aegis SDK的一些逻辑在执行的时候受到了影响,导致实际执行时间要晚于设定的时间,所以上报的“首屏耗时”其实要比实际晚的。
用户的页面会在首屏完成后,继续加载很多DOM元素,也就是有很多DOM元素的变化,导致了Aegis SDK计算出来的首屏时间也要晚于真实的“首屏时间”。
CLS指标优化
在一开始就确定列表高度(加入分页),通过骨架屏优化加载效果,同时减少DOM变化。
广告挂件使用绝对布局,使其脱离文档流,减少DOM变化。
一些其他元素,如图片等,确定长度和宽度属性,这些值允许浏览器在将图像渲染到位之前保留视觉空间。
一些元素的变化,通过CSS实现,而不是使用JS改变元素属性实现。
总结
作者简介
李振
腾讯云高级工程师
李振,腾讯云高级工程师,腾讯内部最受欢迎的前端开源项目TAM负责人,腾讯云前端性能监控RUM负责人,有丰富的前端开发和产品研发经验。
点击「阅读原文」,立即体验RUM~