如何使用 Performance API 让您的网站更快

前端劝退师

共 8546字,需浏览 18分钟

 ·

2021-06-24 17:33

本教程介绍了如何使用 Performance API 记录来自访问您的应用程序的真实用户的类似 DevTool 的统计信息。

使用浏览器 DevTools 评估 Web 应用程序性能很有用,但复制实际使用情况并不容易。使用不同设备、浏览器和网络的不同地点的人都会有不同的体验。

性能 API 简介

性能API使用缓冲记录DevTool般在你的网页的生命周期的特定点对象属性指标。这些要点包括:

  1. 页面导航:记录页面加载重定向、连接、握手、DOM 事件等。

  2. 资源加载:记录图片、CSS、脚本、Ajax 调用等资源加载。

  3. Paint metrics:记录浏览器渲染信息。

  4. 自定义性能:记录任意应用程序处理时间以查找慢功能。

所有 API 都在客户端 JavaScript 中可用,包括Web Workers。您可以使用以下方法检测 API 支持:

if ('performance' in window) {

// call Performance APIs

}

注意:请注意,尽管实现了大部分 API,但 Safari 并不支持所有方法。

自定义(用户)性能 API 也复制到:

  • Node.js 内置performance_hook模块,以及

  • Deno性能 API(使用它的脚本必须在--allow-hrtime许可下运行)。

Date()不够好?

您可能已经看过使用该Date()函数记录经过时间的示例。例如:

const start = new Date();

// ... run code ...

const elapsed = new Date() - start;

但是,Date()计算仅限于最接近的毫秒并基于系统时间,操作系统可以随时更新系统时间。

Performance API 使用一个单独的、更高分辨率的计时器,可以在几分之一毫秒内进行记录。它还提供了以其他方式无法记录的指标,例如重定向和 DNS 查找时间。

记录性能指标

如果您可以将其记录在某处,则在客户端代码中计算性能指标非常有用。您可以使用 Ajax Fetch / XMLHttpRequest请求或Beacon API将统计信息发送到您的服务器进行分析。

或者,大多数分析系统提供自定义事件类 API 来记录时间。例如,Google Analytics User Timings API可以DOMContentLoaded通过传递类别 ( 'pageload')、变量名称 ( "DOMready") 和值来记录时间:

const pageload = performance.getEntriesByType( 'navigation' )[0];

ga('send', 'timing', 'pageload', 'DOMready', pageload.domContentLoadedEventStart);

此示例使用页面导航计时 API。所以让我们从那里开始……

页面导航时序

在快速连接上测试您的网站不太可能代表用户体验。浏览器 DevTools网络选项卡允许您调节速度,但它无法模拟较差或断断续续的 3G 信号。

Navigation Timing API 将单个PerformanceNavigationTiming对象推送到性能缓冲区。它包含真实用户观察到的有关重定向、加载时间、文件大小、DOM 事件等的信息。

通过运行访问对象:

const pagePerf = performance.getEntriesByType( 'navigation' );

或者通过将页面 URL ( window.location)传递给 来访问它getEntriesByName() method

const pagePerf = performance.getEntriesByName( window.location );

两者都返回一个包含具有只读属性的对象的单个元素的数组。例如:

[
{
name: "https://site.com/",
initiatorType: "navigation",
entryType: "navigation",
initiatorType: "navigation",
type: "navigate",
nextHopProtocol: "h2",
startTime: 0
...
}
]

该对象包括资源标识属性

财产描述
名称资源网址
条目类型性能类型——"navigation"对于页面,"resource"对于资产
发起者类型启动下载的资源 -"navigation"用于页面
下一跳协议网络协议
服务器定时PerformanceServerTiming对象数组

注意:performanceServerTiming namedescriptiondurationmetrics由服务器响应写入 HTTPServer-Timing标头

该对象包括相对于页面加载开始的以毫秒为单位的资源计时属性。通常按以下顺序预计时间:

财产描述
开始时间获取开始时的时间戳 -0用于页面
工人开始启动 Service Worker 之前的时间戳
重定向开始第一次重定向的时间戳
重定向结束收到上次重定向的最后一个字节后的时间戳
获取开始获取资源之前的时间戳
域查找开始DNS 查找前的时间戳
域查找结束DNS 查找后的时间戳
连接开始建立服务器连接前的时间戳
连接结束建立服务器连接后的时间戳
安全连接启动SSL 握手前的时间戳
请求开始浏览器请求前的时间戳
响应开始浏览器收到第一个字节数据时的时间戳
响应结束收到最后一个字节数据后的时间戳
期间startTimeresponseEnd之间经过的时间

该对象包括以字节为单位下载大小属性

财产描述
传输大小资源大小,包括标题和正文
编码体尺寸解压前的资源体大小
解码体尺寸解压后的资源体大小

最后,该对象包括进一步的导航和 DOM 事件属性(在 Safari 中不可用):

财产描述
类型要么"navigate""reload""back_forward"或者"prerender"
重定向计数重定向次数
卸载事件开始unload上一个文档事件之前的时间戳
卸载事件结束unload上一个文档事件之后的时间戳
domInteractiveHTML 解析和 DOM 构建完成时的时间戳
domContentLoadedEventStart

运行DOMContentLoaded事件处理程序之前的时间戳

domContentLoadedEventEnd

运行DOMContentLoaded事件处理程序后的时间戳

domCompleteDOM 构建和DOMContentLoaded事件完成时的时间戳
加载事件开始页面load事件触发前的时间戳
加载事件结束页面load事件后的时间戳。下载所有资产

在页面完全加载后记录页面加载指标的示例:

'performance' in window && window.addEventListener('load', () => {

const
pagePerf = performance.getEntriesByName( window.location )[0],
pageDownload = pagePerf.duration,
pageDomComplete = pagePerf.domComplete;

});

页面资源时序

PerformanceResourceTiming每当页面加载图像、字体、CSS 文件、JavaScript 文件或任何其他项目等资产时,Resource Timing API 都会将对象推送到性能缓冲区。跑步:

const resPerf = performance.getEntriesByType( 'resource' );

这将返回一组资源计时对象。这些具有与上面显示页面计时相同的属性,但没有导航和 DOM 事件信息。

这是一个示例结果:

[
{
name: "https://site.com/style.css",
entryType: "resource",
initiatorType: "link",
fetchStart: 150,
duration: 300
...
},
{
name: "https://site.com/script.js",
entryType: "resource",
initiatorType: "script",
fetchStart: 302,
duration: 112
...
},
...
]

可以通过将其 URL 传递给.getEntriesByName()方法来检查单个资源:

const resourceTime = performance.getEntriesByName('https://site.com/style.css');

这将返回一个包含单个元素的数组:

[
{
name: "https://site.com/style.css",
entryType: "resource",
initiatorType: "link",
fetchStart: 150,
duration: 300
...
}
]

您可以使用 API 报告每个 CSS 文件的加载时间和解压缩大小:

// array of CSS files, load times, and file sizes
const css = performance.getEntriesByType('resource')
.filter( r => r.initiatorType === 'link' && r.name.includes('.css'))
.map( r => ({

name: r.name,
load: r.duration + 'ms',
size: r.decodedBodySize + ' bytes'

}) );

css数组现在包含每个 CSS 文件的对象。例如:

[
{
name: "https://site.com/main.css",
load: "155ms",
size: "14304 bytes"
},
{
name: "https://site.com/grid.css",
load: "203ms",
size: "5696 bytes"
}
]

注意:负载和大小为零表示资产已被缓存。

至少 150 个资源指标对象将被记录到性能缓冲区。您可以使用.setResourceTimingBufferSize(N)方法定义一个特定的数字。例如:

// record 500 resources
performance.setResourceTimingBufferSize(500);

可以使用 清除现有指标.clearResourceTimings() method

浏览器绘制时间

First Contentful Paint (FCP)衡量用户导航到您的页面后呈现内容所需的时间。Chrome 的 DevTool Lighthouse 面板的Performance部分显示了该指标。Google 认为 FCP 时间少于 2 秒是好的,并且您的页面将比 Web 的 75% 显示得更快。

在以下情况下,Paint Timing API 将两个记录两个PerformancePaintTiming对象推送到性能缓冲区:

  • first-paint发生:浏览器绘制第一个像素,并且

  • first-contentful-paint发生:浏览器绘制 DOM 内容的第一项

运行时,两个对象都以数组形式返回:

const paintPerf = performance.getEntriesByType( 'paint' );

结果示例:

[
{
"name": "first-paint",
"entryType": "paint",
"startTime": 125
},
{
"name": "first-contentful-paint",
"entryType": "paint",
"startTime": 127
}
]

开始时间是相对于初始页面加载。

用户计时

Performance API 可用于为您自己的应用程序功能计时。所有用户计时方法都可以在客户端 JavaScript、Web Workers、Deno 和 Node.js 中使用。

请注意,Node.js 脚本必须加载Performance hooks( perf_hooks) 模块

CommonJSrequire语法:

const { performance } = require('perf_hooks');

或者 ES 模块import语法:

import { performance } from 'perf_hooks';

最简单的选项是performance.now(),它返回进程生命周期开始时的高分辨率时间戳。

您可以performance.now()用于简单的计时器。例如:

const start = performance.now();

// ... run code ...

const elapsed = performance.now() - start;

注意:非标准timeOrigin属性返回 Unix 时间的时间戳。它可以在 Node.js 和浏览器 JavaScript 中使用,但不能在 IE 和 Safari 中使用。

performance.now()在管理多个计时器时很快变得不切实际。该.mark()方法将一个命名的PerformanceMark 对象对象添加到性能缓冲区。例如:

performance.mark('script:start');

performance.mark('p1:start');
// ... run process 1 ...
performance.mark('p1:end');

performance.mark('p2:start');
// ... run process 2 ...
performance.mark('p2:end');

performance.mark('script:end');

以下代码返回一个标记对象数组:

const marks = performance.getEntriesByType( 'mark' );

entryTypenamestartTime属性:

[
{
entryType: "mark",
name: "script:start",
startTime: 100
},
{
entryType: "mark",
name: "p1:start",
startTime: 200
},
{
entryType: "mark",
name: "p1:end",
startTime: 300
},
...
]

可以使用该.measure()方法计算两个标记之间经过的时间。它传递了一个度量名称、开始标记名称(或null使用零)和结束标记名称(或null使用当前时间):

performance.measure('p1', 'p1:start', 'p1:end');
performance.measure('script', null, 'script:end');

每次调用都会将具有计算持续时间的PerformanceMeasure对象推送到性能缓冲区。可以通过运行来访问一系列度量:

const measures = performance.getEntriesByType( 'measure' );

例子:

[
{
entryType: "measure",
name: "p1",
startTime: 200,
duration: 100
},
{

entryType: "measure",
name: "script",
startTime: 0,
duration: 500
}
]

可以使用以下.getEntriesByName()方法按名称检索标记或测量对象:

performance.getEntriesByName( 'p1' );

其他方法:

  • .getEntries(): 返回所有性能条目的数组。

  • .clearMarks( [name] ): 清除命名标记(不带名称运行以清除所有标记)

  • .clearMeasures( [name] ): 清除已命名的度量(不带名称运行以清除所有度量)

一个PerformanceObserver可以观看更改到缓冲区,并运行一个函数,当特定对象出现。观察者函数定义有两个参数:

  1. list: 观察者条目

  2. observer(可选观察者对象


function performanceHandler(list, observer) {

list.getEntries().forEach(entry => {

console.log(`name : ${ entry.name }`);
console.log(`type : ${ entry.type }`);
console.log(`duration: ${ entry.duration }`);

// other code, e.g.
// send data via an Ajax request

});

}

这个函数被传递给一个新PerformanceObserver对象。该.observe()方法然后将观察到的entryTypes(通常"mark""measure"和/或"resource"):

let observer = new PerformanceObserver( performanceHandler );
observer.observe( { entryTypes: [ 'mark', 'measure' ] } );

performanceHandler()每当将新标记或度量对象推送到性能缓冲区时,该函数就会运行。

自我分析 API

自我剖析API是关系到性能API和可以帮助找到低效或不必要的后台功能,而无需手动设置标志和措施。

示例代码:

// new profiler, 10ms sample rate
const profile = await performance.profile({ sampleInterval: 10 });

// ... run code ...

// stop profiler, get trace
const trace = await profile.stop();

跟踪返回有关在每个采样间隔执行的脚本、函数和行号的数据。重复引用相同的代码可能表明进一步优化是可能的。

API 目前正在开发中(请参阅Chrome 状态)并且可能会发生变化。

调整应用程序性能

性能 API 提供了一种方法来衡量网站和应用程序在真实设备上的网站和应用程序速度,这些设备由真实的人在不同位置使用一系列连接。它可以轻松地为每个人整理类似 DevTool 的指标并识别潜在的瓶颈。

解决这些性能问题是另一回事,但SitePoint Jump Start Web Performance 一书会有所帮助。它提供了一系列快餐、简单的食谱和改变生活的饮食,让您的网站更快、响应更快。

往期推荐

Vite 太快了,烦死了,是时候该小睡一会了。


如何实现比 setTimeout 快 80 倍的定时器?


万字长文!总结Vue 性能优化方式及原理


90 行代码的 webpack,你确定不学吗?


最后





如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 欢迎加我微信「huab119」拉你进技术群,长期交流学习...

    关注公众号「前端劝退师」,持续为你推送精选好文,也可以加我为好友,随时聊骚。



点个在看支持我吧,转发就更好了



浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报