大家好,今天咱们聊聊JS Performance API
:打造你的用户体验监控神器!
各位观众老爷们,大家好!今天咱们不聊八卦,不谈风月,就来点实在的,聊聊如何用JS的 Performance API
打造属于你自己的用户体验监控系统。这玩意儿可不是什么高不可攀的黑科技,只要你稍微花点心思,就能让你的网站性能一览无余,用户体验蹭蹭往上涨。
咱们先来想想,啥叫用户体验?简单来说,就是用户在使用你的网站或者应用时的感觉。卡不卡?加载快不快?操作顺不顺手?这些都直接影响用户体验。而 Performance API
就是你的眼睛,帮你看到这些问题,让你能对症下药。
1. Performance API
是个啥?
Performance API
是一组 JavaScript 接口,它允许你访问有关当前页面性能的各种信息。它提供了测量和分析页面加载时间、资源加载时间、内存使用情况等等的能力。有了它,你就能像福尔摩斯一样,抽丝剥茧,找出性能瓶颈。
核心对象:window.performance
所有好戏都从 window.performance
这个对象开始。它就像一个百宝箱,里面装着各种性能数据。
一些常用的属性和方法:
performance.timing
: 包含页面加载过程中各个阶段的时间戳信息。这个是老朋友了,但依然很有用。performance.navigation
: 提供有关用户如何导航到页面的信息(例如,是通过链接点击、刷新还是历史记录)。performance.getEntries()
: 返回一个PerformanceEntry
对象的列表,这些对象记录了各种性能事件,比如资源加载、标记和度量。performance.mark()
: 允许你创建自定义的性能标记,用来测量特定代码段的执行时间。performance.measure()
: 允许你测量两个标记之间的持续时间。performance.memory
: 报告 JavaScript 堆内存使用情况(注意:这个API可能在某些浏览器中被弃用或限制使用)。
2. 摸清页面加载时间:performance.timing
的妙用
performance.timing
包含了一堆时间戳,记录了页面加载过程中发生的各种事件的时间。
举个例子:
const timing = performance.timing;
console.log("解析 DOM 树结构开始时间:", timing.domInteractive);
console.log("DOM 树结构解析完成时间:", timing.domComplete);
console.log("页面完全加载时间:", timing.loadEventEnd);
通过这些时间戳,你可以计算出:
- DNS 查询时间:
domainLookupEnd - domainLookupStart
- TCP 连接时间:
connectEnd - connectStart
- 请求响应时间:
responseEnd - requestStart
- DOM 解析时间:
domComplete - domInteractive
- 页面完全加载时间:
loadEventEnd - navigationStart
代码示例:计算页面加载时间
function calculatePageLoadTime() {
const timing = performance.timing;
const dnsLookupTime = timing.domainLookupEnd - timing.domainLookupStart;
const tcpConnectTime = timing.connectEnd - timing.connectStart;
const requestResponseTime = timing.responseEnd - timing.requestStart;
const domParseTime = timing.domComplete - timing.domInteractive;
const pageLoadTime = timing.loadEventEnd - timing.navigationStart;
console.log("DNS 查询时间:", dnsLookupTime, "ms");
console.log("TCP 连接时间:", tcpConnectTime, "ms");
console.log("请求响应时间:", requestResponseTime, "ms");
console.log("DOM 解析时间:", domParseTime, "ms");
console.log("页面完全加载时间:", pageLoadTime, "ms");
return {
dnsLookupTime,
tcpConnectTime,
requestResponseTime,
domParseTime,
pageLoadTime,
};
}
const loadTimes = calculatePageLoadTime();
注意事项:
performance.timing
已经被PerformanceNavigationTiming
取代,但为了兼容性,仍然可以使用。- 时间戳的单位是毫秒。
- 这些数据只是近似值,受到网络状况、浏览器性能等因素的影响。
3. 精确测量:performance.mark()
和 performance.measure()
performance.mark()
允许你在代码中打上标记,performance.measure()
则可以测量两个标记之间的持续时间。这对于测量特定代码段的执行时间非常有用。
代码示例:测量函数执行时间
function myExpensiveFunction() {
// 模拟一个耗时的操作
let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
return sum;
}
function measureFunctionExecutionTime(func) {
performance.mark("functionStart");
func();
performance.mark("functionEnd");
performance.measure("functionExecution", "functionStart", "functionEnd");
const measurements = performance.getEntriesByName("functionExecution");
const duration = measurements[0].duration;
console.log(`函数 ${func.name} 执行时间:`, duration, "ms");
performance.clearMarks("functionStart");
performance.clearMarks("functionEnd");
performance.clearMeasures("functionExecution");
return duration;
}
const executionTime = measureFunctionExecutionTime(myExpensiveFunction);
解释一下:
performance.mark("functionStart")
: 在函数执行前打上一个标记。func()
: 执行你要测量的函数。performance.mark("functionEnd")
: 在函数执行后打上另一个标记。performance.measure("functionExecution", "functionStart", "functionEnd")
: 测量functionStart
和functionEnd
之间的持续时间,并将其命名为 "functionExecution"。performance.getEntriesByName("functionExecution")
: 获取所有名为 "functionExecution" 的性能条目。measurements[0].duration
: 获取第一个性能条目的持续时间。performance.clearMarks()
和performance.clearMeasures()
: 清理标记和测量结果,避免干扰后续的测量。
应用场景:
- 测量组件渲染时间。
- 测量 API 请求时间。
- 测量复杂算法的执行时间。
- 诊断性能瓶颈。
4. 追踪资源加载:performance.getEntriesByType()
performance.getEntriesByType()
允许你获取特定类型的性能条目,比如 "resource"。这对于追踪资源加载时间非常有用。
代码示例:获取所有图片加载时间
function getImagesLoadTimes() {
const resources = performance.getEntriesByType("resource");
const imageLoadTimes = resources
.filter((resource) => resource.initiatorType === "img")
.map((resource) => ({
name: resource.name,
duration: resource.duration,
}));
console.table(imageLoadTimes);
return imageLoadTimes;
}
const images = getImagesLoadTimes();
解释一下:
performance.getEntriesByType("resource")
: 获取所有资源类型的性能条目。resource.initiatorType === "img"
: 过滤出initiatorType
为 "img" 的资源,也就是图片。resource.name
: 图片的 URL。resource.duration
: 图片加载时间。console.table(imageLoadTimes)
: 以表格的形式打印图片加载时间。
应用场景:
- 找出加载时间过长的图片或脚本。
- 优化资源加载顺序。
- 监控第三方资源的性能。
5. 监控用户体验:自定义性能指标
除了使用 Performance API
提供的标准指标外,你还可以创建自定义的性能指标来更好地监控用户体验。
一些常用的自定义指标:
- 首次内容绘制 (First Contentful Paint, FCP): 浏览器首次绘制任何文本、图像、非白色画布或 SVG 的时间点。
- 最大内容绘制 (Largest Contentful Paint, LCP): 视口中最大的可见元素完成渲染的时间点。
- 首次输入延迟 (First Input Delay, FID): 用户首次与页面交互(例如,点击链接、按钮或使用自定义的 JavaScript 驱动的控件)到浏览器实际能够响应交互之间的时间。
- Cumulative Layout Shift (CLS): 测量页面上非预期布局移动的视觉稳定性。
代码示例:测量 FCP
function getFCP() {
return new Promise((resolve) => {
new PerformanceObserver((entryList) => {
const paintEntries = entryList.getEntriesByName("first-contentful-paint");
if (paintEntries.length > 0) {
const fcp = paintEntries[0].startTime;
resolve(fcp);
}
}).observe({ type: "paint", buffered: true });
});
}
getFCP().then((fcp) => {
console.log("FCP:", fcp, "ms");
});
代码示例:测量 LCP
function getLCP() {
return new Promise((resolve) => {
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
resolve(lastEntry.startTime);
}).observe({ type: "largest-contentful-paint", buffered: true });
});
}
getLCP().then((lcp) => {
console.log("LCP:", lcp, "ms");
});
代码示例:测量 FID
function getFID() {
return new Promise((resolve) => {
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const fid = entries[0].processingStart - entries[0].startTime;
resolve(fid);
}).observe({ type: "first-input", buffered: true });
});
}
getFID().then((fid) => {
console.log("FID:", fid, "ms");
});
解释一下:
PerformanceObserver
: 监听特定类型的性能事件。entryList.getEntries()
: 获取所有性能条目。entryList.getEntriesByName()
: 获取指定名称的性能条目。startTime
: 事件发生的时间。duration
: 事件的持续时间。
注意事项:
- 这些指标需要使用
PerformanceObserver
来监听,因为它们是在页面加载过程中动态变化的。 - 这些指标的计算方法可能会因浏览器而异。
6. 数据上报:将性能数据发送到服务器
有了性能数据,下一步就是将它们发送到服务器,以便进行分析和监控。
一些常用的数据上报方式:
XMLHttpRequest
(XHR): 传统的 Ajax 请求。fetch API
: 更现代的 Ajax 请求。navigator.sendBeacon()
: 专门用于发送分析数据的 API,即使页面即将卸载也能可靠地发送数据。
代码示例:使用 navigator.sendBeacon()
上报性能数据
function reportPerformanceData(data) {
const url = "/api/performance"; // 你的服务器端点
const blob = new Blob([JSON.stringify(data)], {
type: "application/json",
});
navigator.sendBeacon(url, blob);
}
window.addEventListener("load", () => {
const performanceData = {
pageLoadTime: calculatePageLoadTime(),
fcp: getFCP(),
lcp: getLCP(),
fid: getFID(),
};
Promise.all([
performanceData.fcp,
performanceData.lcp,
performanceData.fid,
]).then((values) => {
performanceData.fcp = values[0];
performanceData.lcp = values[1];
performanceData.fid = values[2];
reportPerformanceData(performanceData);
});
});
解释一下:
reportPerformanceData(data)
: 将性能数据发送到服务器。url
: 你的服务器端点。blob
: 将数据转换为 Blob 对象,以便发送。navigator.sendBeacon(url, blob)
: 使用navigator.sendBeacon()
发送数据。window.addEventListener("load", ...)
: 在页面加载完成后执行代码。calculatePageLoadTime()
,getFCP()
,getLCP()
,getFID()
: 获取性能数据。Promise.all(...)
: 等待所有异步操作完成。reportPerformanceData(performanceData)
: 将性能数据发送到服务器。
注意事项:
navigator.sendBeacon()
是异步的,不会阻塞页面卸载。- 服务器端需要接收和处理这些数据。
7. 总结:打造你的用户体验监控系统
通过 Performance API
,你可以:
- 监控页面加载时间。
- 测量特定代码段的执行时间。
- 追踪资源加载时间。
- 创建自定义性能指标。
- 将性能数据发送到服务器。
有了这些工具,你就可以打造一个属于你自己的用户体验监控系统,让你的网站性能一览无余,用户体验蹭蹭往上涨!
性能指标参考:
指标 | 描述 | 目标值 (越低越好) |
---|---|---|
First Contentful Paint (FCP) | 浏览器首次渲染任何内容的时间 | 1.8 秒以内 |
Largest Contentful Paint (LCP) | 视口中最大的内容元素完成渲染的时间 | 2.5 秒以内 |
First Input Delay (FID) | 用户首次与页面交互到浏览器响应之间的时间,衡量交互性 | 100 毫秒以内 |
Cumulative Layout Shift (CLS) | 页面视觉稳定性指标,衡量页面布局的意外偏移 | 0.1 以内 |
Time to First Byte (TTFB) | 浏览器接收到服务器响应的第一个字节的时间,衡量服务器响应速度 | 0.8 秒以内 |
Total Blocking Time (TBT) | 页面加载过程中,阻止用户交互的总时间,是 FID 的一个替代指标(在实验室中) | 300 毫秒以内 |
一些建议:
- 定期监控你的网站性能。
- 设置性能预算,以便及时发现问题。
- 使用自动化工具来收集和分析性能数据。
- 根据性能数据来优化你的网站。
最后,希望今天的分享对大家有所帮助。记住,用户体验至上! 咱们下期再见!