性能优化很重要,可是咱们怎么知道本身的网页性能如何呢?如何测算?很多人会说,我打开很快呀,不到1秒,可是别人呢?css
要正确地分析咱们网页应用的性能指标,这种方法比较靠谱的呢!html
横轴表明加载时间,纵轴表明用户数(其实还能够加上地区等指标的筛选,It's up to you!).只有这样咱们才能避免坐井观天,看到咱们的应用在全部用户中的加载状况。react
咱们知道,用GA能够采集不少信息,包括页面停留时间,DOM加载时间,以及页面加载时间等等,可是,评判一个网页的性能,是有优先级之分的。git
Any performance metric that values all the content the same is not a good metric.github
有哪些指标是咱们须要关注的呢?当用户导航到网页时,一般会寻找视觉反馈,以确信一切符合预期。web
体验 | 指标 | 具体状况 |
---|---|---|
是否发生? | 首次绘制 (FP)/首次内容绘制 (FCP) | 导航是否成功启动?服务器是否有响应? |
是否有用? | 首次有效绘制 (FMP)/主角元素计时 | 是否已渲染能够与用户互动的足够内容? |
是否可用? | 可交互时间 (TTI) | 用户能够与页面交互,仍是页面仍在忙于加载? |
是否使人愉快? | 耗时较长的任务(Long Task) | 交互是否顺畅而天然,没有滞后和卡顿? |
下列加载时间线屏幕截图有助于您更直观地了解加载指标对应的加载体验:(lighthouse检查结果截图)
性能优化
咱们能够用Lighthouse和 Web Page Test 等这些工具用于在功能发布前测试其性能,但这些工具并未在用户设备上运行,所以未反映出用户的实际性能体验。bash
这里咱们用几个新增的API,他们是PerformanceObserver、PerformanceEntry 和 DOMHighResTimeStamp 以及 performance。服务器
如下是PerformanceEntry的可选项:async
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// `name` will be either 'first-paint' or 'first-contentful-paint'.
const metricName = entry.name;
const time = Math.round(entry.startTime + entry.duration);
//发送数据到后台
request.post({
eventCategory:'Performance Metrics',
eventAction: metricName,
eventValue: time,
nonInteraction: true,
})
}
});
observer.observe({entryTypes: ['paint']});
复制代码
能够看到用PerformanceObserver观察entryTypes为paint,也就是FP/FCP的timing.每个entry是一个PerformanceEntry对象。
肯定页面上的主角元素以后,您能够跟踪为用户呈现这些元素的时间点。
记住!不管用什么方法,你应该尽快让主角元素呈现出来
<img src="hero.jpg" onload="performance.clearMarks('img displayed'); performance.mark('img displayed');">
<script>
performance.clearMarks("img displayed");
performance.mark("img displayed");
</script>
复制代码
假如咱们的主角元素是一张图片,这时候咱们在图片加载出来前先mark一下,而后在onload时间在mark一下,二者再相减,就能够知道咱们主角元素出来的用时了
这里咱们使用tti-polyfill
import ttiPolyfill from 'tti-polyfill';
ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
request.post({
eventCategory:'Performance Metrics',
eventAction:'TTI',
eventValue: tti,
nonInteraction: true,
});
});
复制代码
getFirstConsistentlyInteractive() 方法接受可选的 startTime 配置选项,让您能够指定下限值(您知道您的应用在此以前没法进行交互)。 默认状况下,该 polyfill 使用 DOMContentLoaded 做为开始时间,但一般状况下,使用主角元素呈现的时刻或您知道全部事件侦听器都已添加的时间点这类时间会更准确。
Long time在 style layout, paint,script,都会出现。
咱们能够在Dev Tools找到存在的long-task。
要在 JavaScript 中检测耗时较长的任务,须要建立新的 PerformanceObserver,并观察类型为 longtask 的条目。 耗时较长的任务条目的一个优势是包含提供方属性,有助于您更轻松地追查致使出现耗时较长任务的代码:
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
request.post({
eventCategory:'Performance Metrics',
eventAction: 'longtask',
eventValue:Math.round(entry.startTime + entry.duration),
eventLabel:JSON.stringify(entry.attribution),
});
}
});
observer.observe({entryTypes: ['longtask']});
复制代码
若要在代码中检测输入延迟,您可将事件时间戳与当前时间做比较,若是二者相差超过 100 毫秒,您能够并应该进行报告。
const testBtn = document.querySelector('#testBtn');
testBtn.addEventListener('click', (event) => {
// Event listener logic goes here...
const lag = performance.now() - event.timeStamp;
if (lag > 100) {
request.post({
eventCategory:'Performance Metric'
eventAction: 'input-latency',
eventLabel: '#subscribe:click',
eventValue:Math.round(lag),
nonInteraction: true,
});
}
});
复制代码
有些时候,用户会由于页面加载时间过长而选择离开,可是他到底忍受了多久呢?
咱们能够用visibilitychange(该事件在页面卸载或进入后台时触发)记录
<script>
window.trackLeaver = () => {
// Remove the listener so it only runs once.
document.removeEventListener('visibilitychange', window.trackLeaver);
// 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('&'));
};
document.addEventListener('visibilitychange', window.trackLeaver);
</script>
复制代码
直接在全部css文件后加一下performace.mark就能够了, 不过内联的css或者async js都不行哦。
使用 fontfaceobserver,
performance.clearMarks("img displayed");
performance.mark("img displayed");
new w.FontFaceObserver( "lato" )
.check()
.then( function(){ console.log( “Loaded!” );
performance.clearMarks("img displayed");
performance.mark("img displayed");});
复制代码
方法相似于采集主角元素的加载时间。
function onRenderCallback(
id, // the "id" prop of the Profiler tree that has just committed
phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
actualDuration, // time spent rendering the committed update
baseDuration, // estimated time to render the entire subtree without memoization
startTime, // when React began rendering this update
commitTime, // when React committed this update
interactions // the Set of interactions belonging to this update
) {
// Aggregate or log render timings...
}
复制代码
在RenderCallback里面上报就能够了。
咱们作优化必定是为了有效果,若是咱们作出来最后的效果还不如之前就坏了,因此咱们要本身先测试,而后先灰度观察,再全量。
这里方法只是为了有机会帮助咱们制定优化策略,要取决于咱们的人力,时间,技术等成本。