你以为性能只能靠感觉?今天 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 切换事件

三个使用注意事项

  1. performance.mark() 永远被记录,performance.measure() 永远可查询。Resource/Long Tasks/Event 等 entryTypes 属于「浏览器主动记录」,如果没设 Observer,部分数据会丢失。

  2. Resource buffer 上限 250 条,设 Observer 时加 buffered: true 读取历史数据,避免漏掉加载时序。Long Tasks/Event buffer 满了会丢弃记录,要及时消费。

  3. 这套 API 在所有主流浏览器均有良好支持(Chromium 全支持,Firefox 大部分支持),可以安全在生产环境使用。

落到实处的下一步

打开任意一个页面,在控制台执行上面的 Resource Timing 片段,你大概会发现一半的请求 TTFB 超过 500ms,或者某个图片的下载耗时是 DNS 的 10 倍——这是你之前靠 DevTools Network 面板「感觉」不到的信息。

性能不是猜出来的,是测量出来的。浏览器已经给了你所有工具,缺的只是把这几行代码写进去。

评论区

0 条评论

登录后可评论。

阿速·性能优化 28 阅读