你以为性能只能靠感觉?今天 PerformanceObserver 把这件事彻底透明了
写完代码测性能,你还在靠感觉?还是 Date.now() 打个点、console.time() 套一下、然后发版等用户报 bug?
实际上,Chrome 从很早就开始提供一套完整的浏览器原生性能测量 API——PerformanceObserver,W3C Performance Timeline 标准的一部分,零依赖、零安装、浏览器自带。
三个核心模块,解决三类性能问题
User Timing:给代码打时间戳
performance.mark() 在代码任意位置放置具名时间戳,performance.measure() 计算两个 mark 之间的时间差。
这些数据会自动出现在 Chrome DevTools Performance 面板的时间线里,不需要任何额外操作。
performance.mark(login-start);
// ... 登录逻辑 ...
performance.mark(login-end);
performance.measure(login-cost, login-start, login-end);
观察这些数据:
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
observer.observe({ entryTypes: [mark, measure], buffered: true });
Chrome 102 之后 mark() 支持 detail 属性,存放组件名、版本等元数据,方便归因:
performance.mark(render-finished, { detail: { component: ProductCard, variant: A } });
Resource Timing:每个请求的完整时间线
观察所有网络请求的耗时分解:
new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
const { name, duration, transferSize, renderBlockingStatus } = entry;
const dns = entry.domainLookupEnd - entry.domainLookupStart;
const tcp = entry.connectEnd - entry.connectStart;
const ttfb = entry.responseStart - entry.requestStart;
console.table({ name, duration, transferSize, renderBlockingStatus, dns, tcp, ttfb });
});
}).observe({ type: resource, buffered: true });
每一次 fetch/XHR/图片加载的 DNS 查询、TCP 握手、TLS 握手、请求发送、TTFB、下载耗时,全部有记录。
跨域资源需要服务器返回 Timing-Allow-Origin 头才能拿到完整数据,否则大部分字段返回 0,这是安全限制。
Server Timing:前后端性能数据打通
服务端通过 HTTP 响应头传递性能数据,浏览器自动记录:
Server-Timing: db;desc="Cache Read";dur=23.2, app;desc="Render";dur=8.5
new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
entry.serverTiming.forEach((st) => {
console.log(`${st.name}: ${st.duration}ms`);
});
});
}).observe({ entryTypes: [navigation, resource], buffered: true });
数据库查询耗时、缓存命中率、内部处理时间,直接流到前端 RUM 系统,不需要手动打点。
完整 entryTypes 地图
| entryType | 含义 |
|---|---|
| paint | First Paint / First Contentful Paint |
| largest-contentful-paint | 最大内容绘制元素及时间 |
| event | 事件处理耗时(INP 测量) |
| layout-shift | 布局偏移量(CLS 测量) |
| navigation | TTFB、连接时间等 |
| resource | 每个网络请求的耗时 |
| longtask | 超过 50ms 的长任务 |
| visibility-state | Tab 切换事件 |
三个使用注意事项
-
performance.mark() 永远被记录,performance.measure() 永远可查询。Resource/Long Tasks/Event 等 entryTypes 属于「浏览器主动记录」,如果没设 Observer,部分数据会丢失。
-
Resource buffer 上限 250 条,设 Observer 时加 buffered: true 读取历史数据,避免漏掉加载时序。Long Tasks/Event buffer 满了会丢弃记录,要及时消费。
-
这套 API 在所有主流浏览器均有良好支持(Chromium 全支持,Firefox 大部分支持),可以安全在生产环境使用。
落到实处的下一步
打开任意一个页面,在控制台执行上面的 Resource Timing 片段,你大概会发现一半的请求 TTFB 超过 500ms,或者某个图片的下载耗时是 DNS 的 10 倍——这是你之前靠 DevTools Network 面板「感觉」不到的信息。
性能不是猜出来的,是测量出来的。浏览器已经给了你所有工具,缺的只是把这几行代码写进去。
评论区
登录后可评论。