性能优化是全部前端人的追求,在这条路上,方法多种多样。这篇文章,说一下能够怎样定义性能指标及上报。javascript
FP,全称 First Paint,翻译为首次绘制,是时间线上的第一个时间点,它表明网页的第一个像素渲染到屏幕上所用时间,也就是页面在屏幕上首次发生视觉变化的时间。html
统计逻辑前端
经过performance.getEntriesByType('paint’),取第一个pain的时间。如:java
function getFPTime(){ const timings = performance.getEntriesByType('paint')[0]; return timings ? Math.round(timings.startTime) : null }
FCP,全称 First Contentful Paint,翻译为首次内容绘制,顾名思义,它表明浏览器第一次向屏幕绘内容。node
注意:只有首次绘制文本、图片(包含背景图)、非白色的canvas或SVG时才被算做FCP。web
经过performance.getEntriesByType('paint’),取第二个pain的时间,或者经过Mutation Observer观察到首次节点变更的时间。如:npm
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) } } }); function getFPTime(){ const timings = performance.getEntriesByType('paint'); if(timings.length > 1)return timings[1] return timings ? Math.round(timings.startTime) : null //伪代码,算 DOM 变化时的最小那个时间,即节点首次变更的时间 return Math.round(domEntries.length ? Math.min(...domEntries.map(entry => entry.time)) : 0); }
MutationObserver 理解及使用补充:canvas
FP与FCP这两个指标之间的主要区别是:FP是当浏览器开始绘制内容到屏幕上的时候,只要在视觉上开始发生变化,不管是什么内容触发的视觉变化,在这一刻,这个时间点,叫作FP。数组
相比之下,FCP指的是浏览器首次绘制来自DOM的内容。例如:文本,图片,SVG,canvas元素等,这个时间点叫FCP。浏览器
FP和FCP多是相同的时间,也多是先FP后FCP。
FMP,全称 First Meaningful Paint,翻译为首次有意义的绘制,是页面主要内容出如今屏幕上的时间, 这是用户感知加载体验的主要指标。目前尚无标准化的定义, 由于很难以通用的方式去肯定各类类型页面的关键内容。
目前没有统一逻辑,阿里有个标准为最高可见增量元素,采用深度优先遍历方法,详细可见:https://zhuanlan.zhihu.com/p/44933789
其次,能够自定义,好比通 Mutation Observer 观察页面加载的一段时间(如前20s)内页面节点的变化, 即元素新增/移除/隐藏等变化记录下来,这样能够获得页面元素的可见时间点及元素与可视区域的交叉信息等。
而后,自定义一个计算公式,好比根据元素的类型进行权重取值(div 权重1,img 权重2等), 而后取元素与可视区域的交叉区域面积、可见度、 权重值之间的乘积为元素评分。
根据上面获得的信息, 以时间点为X轴, 该时间点可见元素的评分总和为Y轴, 取最高点对应的最小时间为页面主要内容出如今屏幕上的时间。
FID,全称 First Input Delay,翻译为首次输入延迟,是测量用户首次与您的站点交互时的时间(即当他们单击连接/点击按钮/使用自定义的JavaScript驱动控件时)到浏览器实际可以回应这种互动的时间。
方式一,经过performanceObserver(目前支持性为88.78%)观察类型为first-input的entry,得到其startTime/duration等数便可
方式二,初始化时为特定事件类型(click/touch/keydown)绑定通用统计逻辑事件,开始调用时从event.timeStamp取开始处理的时间(这个时间就是首次输入延迟时间),在事件处理中注册requestIdleCallback事件回调onIdleCallback,当onIdleCallback被执行时,当前时间减开始的event.timeStamp即为duration时间
参考代码:
// 方式一 function getFIDTime(){ const timings = performance.getEntriesByType('first-input')[0]; return timings ? timings : null } // 方式二,如下代码仅表明思路 ['click','touch','keydown'].forEach(eventType => { window.addEventListener(eventType, eventHandle); }); function eventHandle(e) { const eventTime = e.timeStamp; window.requestIdleCallback(onIdleCallback.bind(this, eventTime, e)); } function onIdleCallback(eventTime, e) { const now = window.performance.now(); const duration = now - eventTime; return { duration: Math.round(duration), timestamp: Math.round(eventTime) } ['click','touch','keydown'].forEach(eventType => { window.removeEventListener(eventType, eventHandle); }); }
Event 对象的更多理解:https://developer.mozilla.org/zh-CN/docs/Web/API/Event
TTI,全称 Time To Interactive,翻译为可交互时间,指的是应用在视觉上都已渲染出了,彻底能够响应用户的输入了。是衡量应用加载所需时间并可以快速响应用户交互的指标
与 FMP 相同,很难规范化适用于全部网页的 TTI 指标定义。
统计方式一:谷歌实验室写的npm包,tti-polyfill
import ttiPolyfill from 'tti-polyfill'; ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => { ga('send', 'event', { eventCategory:'Performance Metrics', eventAction:'TTI', eventValue: tti, nonInteraction: true, }); });
统计方式二:在页面加载的必定时间内(如前50s内),以(domContentLoadedEventStart-navigationStart)+5为起始点,循环寻找,找到一个5s的窗口,其中网络请求不超过2个且没有长任务(>50ms),再找到该5秒窗口以前的最后一个长任务,该长任务结束的时间点就是可稳定交互时间。其中长任务可自定义时间或通performance.getEntriesByType('long-task')获取
// 如下代码仅表明思路 const basicTime = 5000; function getTTITime(startTime,longTaskEntries, resourceEntries,domContentLoadedTime) { let busyNetworkInWindow = []; let tti = startTime; while (startTime + basicTime <= 50000) { //从前50s 中去找 tti = startTime; longTasksInWindow = longTaskEntries.filter(task => { return task.startTime < startTime + basicTime && task.startTime + task.duration > startTime; }); if (longTasksInWindow.length) { const lastLongTask = longTasksInWindow[longTasksInWindow.length - 1]; startTime = lastLongTask.startTime + lastLongTask.duration; continue; } busyNetworkInWindow = resourceEntries.filter(request => { return !(request.startTime >= startTime + basicTime || request.startTime + request.duration <= startTime); }); if (busyNetworkInWindow.length > 2) { const firstRequest = busyNetworkInWindow[0]; startTime = firstRequest.startTime + firstRequest.duration; continue; } return Math.max(tti, domContentLoadedTime); } return Math.max(tti, domContentLoadedTime); }
FCI,全称 First CPU Idle,翻译为首次CPU空闲时间表明着一个网页已经知足了最小程度的与用户发生交互行为的时刻。当咱们打开一个网页,咱们并不须要等到一个网页彻底加载好了,每个元素都已经完成了渲染,而后再去与网页进行交互行为。网页知足了咱们基本的交互的时间点是衡量网页性能的一个重要指标。
FCI为在FMP以后,首次在必定窗口时间内没有长任务发生的那一时刻,而且若是这个时间点早于DOMContentLoaded时间,那么FCI的时间为DOMContentLoaded时间,窗口时间的计算函数能够根据Lighthouse提供的计算公式 N = f(t) = 4 e^(-0.045 t) + 1 进行自定义设计
FPS,全称 Frames Per Second,翻译为每秒帧率,表示的是每秒钟画面更新次数,当今大多数设备的屏幕刷新率都是60次/秒。
参考标准:
利用requestAnimationFrame,循环调用,当now大于lastTime+1S时,计算FPS。若小于某个阀值则能够认为当前帧率较差,若连续小于多个阀值,则中止统计,当前页面处于卡顿状态,进入卡顿处理逻辑
更多具体参考:
从window.navigator.userAgent中能够获取用户设备信息,如图:
从window.navigator.connection中能够获取设备网络信息,如:
从window.devicePixelRatio能够获取设备像素比。
pv/uv
监听各类页面切换的状况;SPA页面,能够监听hashChange
性能数据/设备信息/网络情况
用图说话,本文内容,整理到了一张图上。
前端性能优化之自定义性能指标及上报方法详解
前端性能优化之利用 Chrome Dev Tools 进行页面性能分析
Chrome 页面呈现原理与性能优化之企业级分享总结(内附完整ppt)