若是咱们想要提升咱们的前端性能,那么首先你得先只知道怎么测量你的前端性能。可是哪些前端的性能指标咱们应该关注呢? 在Web1.0时代当咱们讨论到前端性能指标用的最多的就是page load time
。到了单页应用程序愈来愈多的时代,单一的page load time
指标已经没法彻底衡量前端性能了,由于一个单页应用程序page load time
可能只触发了一次,可是用户会跟程序有不少的交互,每个交互或者动做都须要有指标来衡量他的性能。javascript
接下来就介绍几个比较重要的前端性能指标:前端
这两个指标对用户来讲都是很重要的,这两个指标能够认为咱们程序正在告诉咱们的用户:咱们正在正确工做。java
这也是很是关键的一个指标,一般若是用户可以快速的看到最重要的一块区域被渲染完成,即便其余的区域都尚未被渲染用户可能也不会注意到。git
指标 | 介绍 |
---|---|
first paint (FP)/first contentful paint (FCP) | 程序是否正确的开始渲染 |
First meaningful paint (FMP) | 用户最关注的的首屏内容显示 |
Time to interactive (TTI) | 程序是否可用 |
long task | 程序使用的体验(是否响应延迟,动画卡顿) |
还有一些其余的指标好比 First Input Delay(首次接受用户响应的延迟时间) First CPU Idle(第一次CPU闲置的时间):这些指标都和上面的指标有着直接的关系。github
知道了这些指标,咱们要把这些指标控制在什么样的时间才能给用户带来比较好的用户的体验呢,下面有这样一张表web
时间 | 介绍 |
---|---|
0到16ms | 用户但愿看的动画可以流畅,动画卡顿会带来很是差的用户体验,在浏览器上每秒钟渲染60帧动画就可以保持流畅,这大约就是16ms渲染一帧,这16ms包括了浏览器要渲染新的元素到页面上须要的时间,也就是说程序有大约10ms的时间能够进行操做。 |
0到100ms | 在这个时间内响应用户的交互,用户会以为响应是很是及时的 |
100到300ms | 用户会感受到有一些延迟 |
300到1000ms | 当执行一些页面加载或者页面跳转的时候,在这个时间内是一个正常的加载跳转时间 |
1000ms或以上 | 超过1000ms(1秒),用户会对以前的操做渐渐失去耐心和注意力 |
10000ms或以上 | 当你的响应超过10秒,用户会感到烦躁,而后终止以前操做 |
上面的延迟时间取决于你使用什么样的网络和设备,好比你使用的电脑和wifi网络,用户在1000ms是一个比较现实的目标。可是当你设备是手机网络只有3G的时候在5000ms内加载才是更现实的目标。canvas
在大多数的时间里,用户在使用程序时大多数的时间都在等待网站响应他们的操做好比点击一个按钮,在文本框中输入内容,而不是等待网站加载。那么网站比较理想的响应用户时间是在100ms之内。浏览器
咱们的目标是在100ms之内响应用户的操做,那为何处理用户的响应时间只有50ms?由于在咱们接受到用户的输入时,可能会有别的任务正在执行.好比咱们接受到用户在文本框中输入了一个A,这个时候浏览器正在执行别的任务,浏览器会把这个操做先加入到任务队列里,等浏览器执行完以前的任务才会去处理用户的响应。那么保守的估计为了让用户在100ms之内得到响应,咱们的处理用户响应的执行时间就是50ms。 性能优化
理论上来讲只要没16ms渲染一帧,动画就会看起来是流畅的,可是浏览器大约须要6ms的时间来将每一帧渲染到画面上.所以产生每一帧动画的时间留给程序的大约就10ms左右。服务器
主线程可以有尽量多的闲置时间,那么当产生用户交互时就能够立马给用户响应。当主线程闲置时,浏览器会有不少的内部程序须要执行,好比闲置GC等。
当页面加载缓慢,用户会失去耐心。网站的加载和响应速度直接影响用户的体验。
在介绍如何在用户真实环境中或者这些指标以前先介绍一些API
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
//这里的name是为了区分'first-paint','first-contentful-paint'
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: metricName,
eventValue: time,
nonInteraction: true,
});
}
});
observer.observe({entryTypes: ['paint']});
复制代码
以前已经介绍过对于浏览器来讲很那准确的知道每一个网站对重要的一块区域显示的时间,那么只有开发者本身可以准确的找到最重要的一块区域得到FMP,假设咱们网站首屏最重要的元素是一个图片就能够这么写.
<img src="important.jpg" onload="performance.clearMarks('img displayed'); performance.mark('img displayed');">
<script> performance.clearMarks("img displayed"); performance.mark("img displayed"); </script>
复制代码
目前在PerformanceObserver中尚未办法得到TTI的接口,经过这个tti-polyfill能够知道这个tti
import ttiPolyfill from 'tti-polyfill.js';
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: 'TTI',
eventValue: tti,
nonInteraction: true,
});
});
复制代码
以前提到过,长任务可能会影响浏览器对用户响应速度或者形成动画的卡顿.那么能意识到长任务的存在而且把他缩短是颇有必要的。(长任务API认为50ms以上任务的为长任务)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
reportToServer({
eventCategory: 'Performance Metrics',
eventAction: 'longtask',
eventValue: Math.round(entry.startTime + entry.duration),
//这里的长任务会包含一个attribute
//https://w3c.github.io/longtasks/#sec-TaskAttributionTiming
eventLabel: JSON.stringify(entry.attribution),
});
}
});
observer.observe({entryTypes: ['longtask']});
复制代码
长任务会阻塞线程致使浏览器没法响应用户操做,以前也提到过若是能在100ms之内响应用户的操做就不会让用户以为卡顿,那么若是可以监控到你关键交互的响应时间也是颇有必要的
const submitBtn = document.querySelector('#submit');
submitBtn.addEventListener('click', (event) => {
const lag = performance.now() - event.timeStamp;
if (lag > 100) {
reportToServer({
eventCategory: 'Performance Metric'
eventAction: 'input-latency',
eventLabel: '#subscribe:click',
eventValue: Math.round(lag),
nonInteraction: true,
});
}
});
复制代码
当咱们的程序若是加载速度很慢(好比加载了过多的js),那么真实用户在网络环境不一致的状况下,有些响应过慢的用户可能早早的在加载完成前就已经退出网站,那么这里就会有一个幸存者误差的问题.你监控的用户都是已经加载完成的用户。为了可以同时检测到退出的用户.
///写在最头部
window.__trackAbandons = () => {
// 去掉事件监听那么这个方法只执行一次
document.removeEventListener('visibilitychange', window.__trackAbandons);
//由于咱们尚未加载report js API 因此咱们要让服务器提供一个post接口在接收此次请求
const ANALYTICS_URL = 'https://ANALYTICS_URL';
const TRACKING_ID = 'TRACKING_ID';
const CLIENT_ID = (Math.random() * Math.pow(2, 52));
// Send the data to Google Analytics via the Measurement Protocol.
navigator.sendBeacon && navigator.sendBeacon(ANALYTICS_URL, [
'v=1', 't=event', 'ec=Load', 'ea=abandon', 'ni=1',
'dl=' + encodeURIComponent(location.href),
'dt=' + encodeURIComponent(document.title),
'tid=' + TRACKING_ID,
'cid=' + CLIENT_ID,
'ev=' + Math.round(performance.now()),
].join('&'));
};
//visibilitychange能够监听页面unload事件
//https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event
document.addEventListener('visibilitychange', window.__trackAbandons);
复制代码
性能对如今的程序愈来愈重要,那么在一个程序须要进行性能优化的时候.我的认为能够按照这样的一个顺序进行:
数据收集->讨论性能指标阀值->针对性能优化->数据从新验证优化结果
这里主要讨论了咱们一些性能指标和收集性能指标的方法,后面会讨论如何针对每一块进行优化。