JS `Performance API`:测量页面加载、资源加载与自定义性能指标

嘿,大家好!我是你们今天的性能优化导师,很高兴能和大家聊聊前端性能监控的大杀器——Performance API。准备好了吗?系好安全带,咱们要起飞咯!

第一站:Performance API 是个啥?为啥我们要用它?

想象一下,你打开一个网页,却发现它慢得像蜗牛爬。用户体验瞬间跌到谷底,用户流失率嗖嗖往上涨。作为前端工程师,我们有责任阻止这种悲剧发生!

Performance API 就是我们手中的利剑,它允许我们访问浏览器底层暴露出来的性能数据,从而了解页面加载的各个阶段、资源的加载情况,甚至可以测量自定义的性能指标。有了它,我们就能精准地定位性能瓶颈,并进行针对性的优化。

简单来说,Performance API 提供了一系列接口,让我们能:

  • 了解页面加载各个阶段的耗时: DNS 查询、TCP 连接、请求发送、服务器响应、DOM 解析等等。
  • 监控资源加载情况: 图片、CSS、JS 文件等等,哪些加载慢了,哪些阻塞了渲染。
  • 自定义性能指标: 例如,某个关键操作的耗时,用户首次交互的时间等等。

第二站:Performance API 的核心成员

Performance API 家族成员众多,但有几个核心成员是必须要认识的:

  • performance 对象: 这是整个 API 的入口,通过 window.performance 可以访问到它。
  • performance.timing 对象(已废弃,不推荐使用): 提供了页面加载各个阶段的时间戳。虽然已经废弃,但为了兼容老代码,还是有必要了解一下。
  • performance.now() 方法: 返回高精度的时间戳,可以用来计算代码块的执行时间。
  • performance.mark()performance.measure() 方法: 用于标记时间点和测量时间间隔,是自定义性能指标的关键。
  • PerformanceObserver 接口: 用于监听性能事件,例如 resourcelongtask 等。
  • Resource Timing API: 获取资源加载的详细信息。
  • Navigation Timing API: 获取页面导航的详细信息。
  • Long Tasks API: 检测耗时较长的任务。

接下来,我们逐一深入了解这些核心成员。

第三站:performance 对象:总司令部

window.performance 对象是 Performance API 的总司令部,所有其他 API 都是通过它来访问的。

const performance = window.performance;

console.log(performance); // 输出一个 Performance 对象

performance 对象本身包含了一些属性和方法,其中最重要的就是 now() 方法和一些用于获取性能条目的方法。

第四站:performance.now():时间旅行者

performance.now() 方法返回一个高精度的时间戳,单位是毫秒。这个时间戳是相对于页面加载开始的时间,而不是相对于 Date.now() 的绝对时间。

const startTime = performance.now();

// 执行一些代码
for (let i = 0; i < 1000000; i++) {
  // 一些耗时的操作
}

const endTime = performance.now();

const elapsedTime = endTime - startTime;

console.log(`代码执行时间:${elapsedTime} 毫秒`);

performance.now() 的精度比 Date.now() 更高,更适合用来测量代码块的执行时间。

第五站:performance.mark()performance.measure():自定义性能指标的利器

performance.mark() 方法用于在时间线上标记一个时间点,而 performance.measure() 方法用于测量两个时间点之间的时间间隔。这两个方法是自定义性能指标的关键。

// 标记开始时间
performance.mark('my-task-start');

// 执行一些代码
for (let i = 0; i < 1000000; i++) {
  // 一些耗时的操作
}

// 标记结束时间
performance.mark('my-task-end');

// 测量时间间隔
performance.measure('my-task', 'my-task-start', 'my-task-end');

// 获取测量结果
const measure = performance.getEntriesByName('my-task')[0];

console.log(`任务执行时间:${measure.duration} 毫秒`);

performance.mark() 接受一个字符串参数,作为时间点的名称。performance.measure() 接受三个参数:

  • name:测量结果的名称。
  • startMark:开始时间点的名称。
  • endMark:结束时间点的名称。

performance.getEntriesByName() 方法可以获取所有名称为 my-task 的性能条目,其中 duration 属性就是测量的时间间隔。

第六站:PerformanceObserver:性能事件的监听者

PerformanceObserver 接口用于监听性能事件,例如 resourcelongtask 等。通过它可以异步地获取性能数据,而不需要轮询 performance 对象。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(entry); // 输出性能条目
  });
});

observer.observe({ entryTypes: ['resource', 'longtask'] });

PerformanceObserver 构造函数接受一个回调函数,当有新的性能条目产生时,回调函数会被调用。回调函数的参数是一个 PerformanceObserverEntryList 对象,可以通过 getEntries() 方法获取所有的性能条目。

observe() 方法用于指定要监听的性能事件类型。entryTypes 属性是一个数组,可以包含以下值:

  • resource:资源加载事件。
  • longtask:长任务事件。
  • mark:标记事件。
  • measure:测量事件。
  • navigation:导航事件。
  • paint:绘制事件。
  • 等等…

第七站:Resource Timing API:资源加载的显微镜

Resource Timing API 提供了资源加载的详细信息,例如 DNS 查询时间、TCP 连接时间、请求发送时间、服务器响应时间等等。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(entry); // 输出资源加载信息
    console.log(`资源 ${entry.name} 加载耗时:${entry.duration} 毫秒`);
    console.log(`DNS 查询耗时:${entry.domainLookupEnd - entry.domainLookupStart} 毫秒`);
    console.log(`TCP 连接耗时:${entry.connectEnd - entry.connectStart} 毫秒`);
    console.log(`请求发送耗时:${entry.requestEnd - entry.requestStart} 毫秒`);
    console.log(`服务器响应耗时:${entry.responseEnd - entry.responseStart} 毫秒`);
  });
});

observer.observe({ entryTypes: ['resource'] });

通过 Resource Timing API,我们可以清楚地了解每个资源的加载过程,从而找到性能瓶颈。

第八站:Navigation Timing API:页面导航的航海日志

Navigation Timing API 提供了页面导航的详细信息,例如 DNS 查询时间、TCP 连接时间、请求发送时间、服务器响应时间、DOM 解析时间等等。虽然 performance.timing 已经废弃,但是 Navigation Timing API 提供了更完善的替代方案。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(entry); // 输出导航信息
    console.log(`DNS 查询耗时:${entry.domainLookupEnd - entry.domainLookupStart} 毫秒`);
    console.log(`TCP 连接耗时:${entry.connectEnd - entry.connectStart} 毫秒`);
    console.log(`请求发送耗时:${entry.requestEnd - entry.requestStart} 毫秒`);
    console.log(`服务器响应耗时:${entry.responseEnd - entry.responseStart} 毫秒`);
    console.log(`DOM 解析耗时:${entry.domComplete - entry.domInteractive} 毫秒`);
    console.log(`页面加载完成耗时:${entry.loadEventEnd - entry.navigationStart} 毫秒`);
  });
});

observer.observe({ entryTypes: ['navigation'] });

通过 Navigation Timing API,我们可以了解页面加载的各个阶段的耗时,从而找到性能瓶颈。

第九站:Long Tasks API:找出幕后黑手

Long Tasks API 用于检测耗时较长的任务,这些任务会阻塞主线程,导致页面卡顿。

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(entry); // 输出长任务信息
    console.log(`长任务耗时:${entry.duration} 毫秒`);
    console.log(`长任务开始时间:${entry.startTime} 毫秒`);
  });
});

observer.observe({ entryTypes: ['longtask'] });

通过 Long Tasks API,我们可以找到导致页面卡顿的幕后黑手,并进行优化。

第十站:实战演练:性能监控的完整流程

现在,我们来模拟一个完整的性能监控流程:

  1. 初始化 PerformanceObserver: 监听 resourcenavigationlongtask 等事件。

    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach((entry) => {
        // 处理性能条目
        if (entry.entryType === 'resource') {
          console.log(`资源 ${entry.name} 加载耗时:${entry.duration} 毫秒`);
        } else if (entry.entryType === 'navigation') {
          console.log(`页面加载完成耗时:${entry.loadEventEnd - entry.navigationStart} 毫秒`);
        } else if (entry.entryType === 'longtask') {
          console.log(`长任务耗时:${entry.duration} 毫秒`);
        }
      });
    });
    
    observer.observe({ entryTypes: ['resource', 'navigation', 'longtask'] });
  2. 自定义性能指标: 使用 performance.mark()performance.measure() 测量关键操作的耗时。

    // 标记开始时间
    performance.mark('my-operation-start');
    
    // 执行一些关键操作
    // ...
    
    // 标记结束时间
    performance.mark('my-operation-end');
    
    // 测量时间间隔
    performance.measure('my-operation', 'my-operation-start', 'my-operation-end');
    
    // 获取测量结果
    const measure = performance.getEntriesByName('my-operation')[0];
    
    console.log(`关键操作执行时间:${measure.duration} 毫秒`);
  3. 数据上报: 将收集到的性能数据上报到服务器,进行分析和可视化。

    const performanceData = {
      resourceTimings: performance.getEntriesByType('resource'),
      navigationTiming: performance.getEntriesByType('navigation'),
      longTasks: performance.getEntriesByType('longtask'),
      customMetrics: performance.getEntriesByType('measure'),
    };
    
    // 发送请求到服务器
    fetch('/api/performance', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(performanceData),
    });
  4. 数据分析和可视化: 在服务器端对性能数据进行分析,找出性能瓶颈,并生成可视化报告。

第十一站:优化策略:性能提升的葵花宝典

有了性能数据,接下来就是进行优化了。以下是一些常见的优化策略:

优化方向 具体策略
资源加载 压缩资源文件、使用 CDN、启用浏览器缓存、优化图片格式、懒加载图片
代码执行 优化 JavaScript 代码、避免阻塞主线程、使用 Web Workers
渲染优化 减少 DOM 操作、使用 CSS Sprites、避免重绘和重排
服务器优化 优化服务器配置、使用 HTTP/2 协议、启用 Gzip 压缩

第十二站:总结与展望

Performance API 是前端性能监控的强大工具,它可以帮助我们了解页面加载的各个阶段、资源的加载情况,甚至可以测量自定义的性能指标。通过分析性能数据,我们可以精准地定位性能瓶颈,并进行针对性的优化,从而提升用户体验。

当然,性能优化是一个持续不断的过程,我们需要不断学习新的技术和方法,才能更好地应对前端性能挑战。

希望今天的分享对大家有所帮助!如果有任何问题,欢迎随时提问。 祝大家都能写出性能卓越的 Web 应用!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注