pin_drop当前位置:知识文库 ❯ 图文
HTML5 API:HTML5 Performance Observe - 完整教程与代码示例
一、教程简介
Performance Observer API 是 Performance Timeline 的一部分,提供了监听性能条目的能力,可以异步获取页面加载、资源请求、用户交互、长任务等各类性能数据。相比轮询 performance.getEntries(),Performance Observer 采用回调机制,只在新的性能条目产生时触发,性能更优,是构建 Web 性能监控系统的核心 API。
二、核心概念
性能条目类型
核心 Web 指标
三、语法与用法
创建与观察
代码示例
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime, entry.duration);
}
});
observer.observe({ type: 'resource', buffered: true });自定义标记与测量
代码示例
performance.mark('start-task');
// ... 执行任务
performance.mark('end-task');
performance.measure('task-duration', 'start-task', 'end-task');
const measure = performance.getEntriesByName('task-duration')[0];
console.log('任务耗时:', measure.duration, 'ms');四、代码示例
示例一:性能监控面板
以下是一个完整的性能监控面板示例,展示了如何使用 Performance API 和 Performance Observer 监控页面性能指标:
代码示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Performance Observer - 性能监控</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #1a1a2e;
min-height: 100vh;
padding: 30px 20px;
color: #e0e0e0;
}
.container { max-width: 800px; margin: 0 auto; }
h1 { font-size: 24px; color: #fff; margin-bottom: 8px; }
.desc { color: #7a8ba0; font-size: 14px; margin-bottom: 24px; }
.metrics {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.metric-card {
background: #16213e;
border-radius: 12px;
padding: 20px;
text-align: center;
border: 1px solid #1a3a5c;
}
.metric-label { font-size: 12px; color: #7a8ba0; margin-bottom: 8px; }
.metric-value { font-size: 28px; font-weight: 700; }
.metric-value.good { color: #00b894; }
.metric-value.warn { color: #fdcb6e; }
.metric-value.bad { color: #e17055; }
.metric-unit { font-size: 14px; color: #7a8ba0; }
.card {
background: #16213e;
border-radius: 12px;
padding: 24px;
margin-bottom: 16px;
border: 1px solid #1a3a5c;
}
.card h2 { font-size: 16px; color: #fff; margin-bottom: 16px; }
.btn-group { display: flex; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
button {
padding: 8px 18px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.btn-cyan { background: #00cec9; color: #1a1a2e; }
.btn-cyan:hover { background: #00b5ad; }
.btn-orange { background: #fdcb6e; color: #1a1a2e; }
.btn-orange:hover { background: #e5b85c; }
.btn-purple { background: #a29bfe; color: #1a1a2e; }
.btn-purple:hover { background: #8c82f7; }
.timing-list { list-style: none; }
.timing-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #1a3a5c;
font-size: 13px;
}
.timing-item:last-child { border-bottom: none; }
.timing-label { color: #7a8ba0; }
.timing-value { color: #00cec9; font-weight: 600; font-family: monospace; }
.resource-table {
width: 100%;
border-collapse: collapse;
}
.resource-table th, .resource-table td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid #1a3a5c;
font-size: 12px;
}
.resource-table th { color: #7a8ba0; font-weight: 500; }
.resource-table td { color: #dfe6e9; }
.custom-timer {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 12px;
}
.timer-display {
font-size: 24px;
font-weight: 700;
color: #00cec9;
font-family: monospace;
min-width: 100px;
}
</style>
</head>
<body>
<div class="container">
<h1>性能监控面板</h1>
<p class="desc">使用 Performance API 和 Performance Observer 监控页面性能指标。</p>
<div class="metrics">
<div class="metric-card">
<div class="metric-label">页面加载</div>
<div class="metric-value good" id="loadTime">--</div>
<div class="metric-unit">ms</div>
</div>
<div class="metric-card">
<div class="metric-label">DOM 就绪</div>
<div class="metric-value good" id="domReady">--</div>
<div class="metric-unit">ms</div>
</div>
<div class="metric-card">
<div class="metric-label">资源数量</div>
<div class="metric-value" id="resourceCount" style="color:#74b9ff;">0</div>
<div class="metric-unit">个</div>
</div>
</div>
<div class="card">
<h2>自定义计时器</h2>
<div class="custom-timer">
<button class="btn-cyan" onclick="startTimer()">开始</button>
<button class="btn-orange" onclick="stopTimer()">停止</button>
<button class="btn-purple" onclick="measureTimer()">测量</button>
<span class="timer-display" id="timerDisplay">0.00 ms</span>
</div>
<ul class="timing-list" id="customMeasures"></ul>
</div>
<div class="card">
<h2>导航时序</h2>
<ul class="timing-list" id="navTiming"></ul>
</div>
<div class="card">
<h2>资源加载</h2>
<div style="max-height:300px;overflow-y:auto;">
<table class="resource-table">
<thead><tr><th>资源</th><th>类型</th><th>耗时</th><th>大小</th></tr></thead>
<tbody id="resourceList"></tbody>
</table>
</div>
</div>
</div>
<script>
// 导航时序
function renderNavTiming() {
const nav = performance.getEntriesByType('navigation')[0];
if (!nav) return;
const timings = [
{ label: 'DNS 查询', value: nav.domainLookupEnd - nav.domainLookupStart },
{ label: 'TCP 连接', value: nav.connectEnd - nav.connectStart },
{ label: '请求发送', value: nav.responseStart - nav.requestStart },
{ label: '响应接收', value: nav.responseEnd - nav.responseStart },
{ label: 'DOM 解析', value: nav.domInteractive - nav.responseEnd },
{ label: 'DOM 完成', value: nav.domComplete - nav.domInteractive },
{ label: '总加载时间', value: nav.loadEventEnd - nav.startTime }
];
document.getElementById('loadTime').textContent = Math.round(nav.loadEventEnd - nav.startTime);
document.getElementById('domReady').textContent = Math.round(nav.domInteractive - nav.startTime);
document.getElementById('navTiming').innerHTML = timings.map(function (t) {
const cls = t.value < 1000 ? 'good' : t.value < 3000 ? 'warn' : 'bad';
return '<li class="timing-item"><span class="timing-label">' + t.label +
'</span><span class="timing-value">' + Math.round(t.value) + ' ms</span></li>';
}).join('');
}
// 资源加载
function renderResources() {
const resources = performance.getEntriesByType('resource');
document.getElementById('resourceCount').textContent = resources.length;
document.getElementById('resourceList').innerHTML = resources.slice(0, 20).map(function (r) {
const name = r.name.split('/').pop().substring(0, 30);
const type = r.initiatorType || 'unknown';
const duration = Math.round(r.duration);
const size = r.transferSize > 0 ? (r.transferSize / 1024).toFixed(1) + ' KB' : '-';
return '<tr><td>' + name + '</td><td>' + type + '</td><td>' + duration + 'ms</td><td>' + size + '</td></tr>';
}).join('');
}
// 自定义计时器
let timerMark = null;
function startTimer() {
performance.mark('timer-start');
timerMark = Date.now();
document.getElementById('timerDisplay').textContent = '运行中...';
}
function stopTimer() {
if (!timerMark) return;
performance.mark('timer-end');
const elapsed = Date.now() - timerMark;
document.getElementById('timerDisplay').textContent = elapsed.toFixed(2) + ' ms';
}
function measureTimer() {
if (!timerMark) return;
stopTimer();
const id = 'measure-' + Date.now();
performance.measure(id, 'timer-start', 'timer-end');
const entries = performance.getEntriesByName(id);
if (entries.length > 0) {
const duration = entries[0].duration.toFixed(2);
const list = document.getElementById('customMeasures');
const item = document.createElement('li');
item.className = 'timing-item';
item.innerHTML = '<span class="timing-label">' + id + '</span><span class="timing-value">' + duration + ' ms</span>';
list.insertBefore(item, list.firstChild);
}
timerMark = null;
document.getElementById('timerDisplay').textContent = '0.00 ms';
}
// Performance Observer 监听新资源
if (typeof PerformanceObserver !== 'undefined') {
try {
const resObserver = new PerformanceObserver(function (list) {
const entries = list.getEntries();
document.getElementById('resourceCount').textContent =
performance.getEntriesByType('resource').length;
});
resObserver.observe({ type: 'resource', buffered: true });
} catch (e) {}
// 监听长任务
try {
const ltObserver = new PerformanceObserver(function (list) {
for (const entry of list.getEntries()) {
console.warn('长任务检测:', entry.duration.toFixed(0) + 'ms', entry.name);
}
});
ltObserver.observe({ type: 'longtask', buffered: true });
} catch (e) {}
}
// 初始化
window.addEventListener('load', function () {
setTimeout(function () {
renderNavTiming();
renderResources();
}, 100);
});
</script>
</body>
</html>五、浏览器兼容性
六、注意事项与最佳实践
1. 使用 buffered 获取历史条目
代码示例
observer.observe({ type: 'resource', buffered: true });
// buffered: true 可以获取观察之前已产生的条目2. 处理不支持的类型
代码示例
try {
const observer = new PerformanceObserver(callback);
observer.observe({ type: 'longtask', buffered: true });
} catch (e) {
console.warn('longtask 类型不支持');
}3. 及时断开观察
代码示例
observer.disconnect();七、代码规范示例
代码示例
class PerformanceMonitor {
constructor() {
this.observers = [];
this.metrics = {};
}
observe(types, callback) {
types.forEach(type => {
try {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => callback(entry));
});
observer.observe({ type, buffered: true });
this.observers.push(observer);
} catch (e) {
console.warn('Performance type not supported:', type);
}
});
}
getNavigationTiming() {
const nav = performance.getEntriesByType('navigation')[0];
return nav ? {
dns: nav.domainLookupEnd - nav.domainLookupStart,
tcp: nav.connectEnd - nav.connectStart,
ttfb: nav.responseStart - nav.requestStart,
domReady: nav.domInteractive - nav.startTime,
load: nav.loadEventEnd - nav.startTime
} : null;
}
measure(name, fn) {
performance.mark(name + '-start');
const result = fn();
performance.mark(name + '-end');
performance.measure(name, name + '-start', name + '-end');
const entry = performance.getEntriesByName(name)[0];
return { result, duration: entry?.duration };
}
async measureAsync(name, fn) {
performance.mark(name + '-start');
const result = await fn();
performance.mark(name + '-end');
performance.measure(name, name + '-start', name + '-end');
const entry = performance.getEntriesByName(name)[0];
return { result, duration: entry?.duration };
}
destroy() {
this.observers.forEach(o => o.disconnect());
this.observers = [];
}
}八、常见问题与解决方案
常见问题
Q1:PerformanceObserver 回调不触发?
原因:可能是类型名称错误或浏览器不支持该类型。
解决方案:使用 try-catch 包裹,检查类型名称是否正确。
Q2:如何获取 Core Web Vitals?
可以通过 Performance Observer 监听特定类型来获取 Core Web Vitals:
代码示例
// LCP
new PerformanceObserver(list => {
const entries = list.getEntries();
console.log('LCP:', entries[entries.length - 1].startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
// CLS
let clsValue = 0;
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (!entry.hadRecentInput) clsValue += entry.value;
});
console.log('CLS:', clsValue);
}).observe({ type: 'layout-shift', buffered: true });
// FID
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
console.log('FID:', entry.processingStart - entry.startTime);
});
}).observe({ type: 'first-input', buffered: true });Q3:如何清理自定义标记?
可以使用以下方法清理自定义标记:
代码示例
performance.clearMarks();
performance.clearMeasures();
performance.clearResourceTimings();九、总结
Performance Observer API 提供了异步监听性能条目的能力,可以高效地获取导航时序、资源加载、自定义测量、长任务等性能数据。通过 buffered: true 可以获取历史条目,配合自定义 mark/measure 可以精确测量代码执行时间。该 API 是构建 Web 性能监控系统的基础,对于优化页面加载速度和用户体验至关重要。
标签:
PerformanceObserver
性能监控
CoreWebVitals
Web性能优化
性能测量
本文涉及AI创作
内容由AI创作,请仔细甄别