📦前端性能信息|Performance

1,227 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

Vue 全局配置 API

根据2.x官方说明,在开发模式中配置:

Vue.config.performance = true;  
// 或 Vue.config.performance = process.env.NODE_ENV !== 'production';

同时配合浏览器(ps:支持 performance.mark API的)开发者工具,可获取组件相关性能信息。

perf.js

这个全局配置的实现,看看2.x源码 src\core\util\perf.js 的具体写法:

利用了Performance API的一些方法,如下perf.measure

// src\core\util\perf.js
import { inBrowser } from './env'

export let mark
export let measure

if (process.env.NODE_ENV !== 'production') {
  // 判断是否支持performance
  const perf = inBrowser && window.performance
  /* istanbul ignore if */
  if (
    perf &&
    perf.mark &&
    perf.measure &&
    perf.clearMarks &&
    perf.clearMeasures
  ) {
    // 用于标记需获取性能信息的节点
    mark = tag => perf.mark(tag)
    // 根据标记测量性能信息,后清除标记
    measure = (name, startTag, endTag) => {
      perf.measure(name, startTag, endTag)
      perf.clearMarks(startTag)
      perf.clearMarks(endTag)
      // perf.clearMeasures(name)
    }
  }
}

对其引用

// src\platforms\web\entry-runtime-with-compiler.js
import { mark, measure } from '../util/perf'
// 标记compile已结束,接着根据开始与结束标记定义一个性能信息测量
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    mark('compile end')
    measure(`vue ${this._name} compile`, 'compile', 'compile end')
}

其余引用可见:

  • src\core\instance\init.js
  • src\core\instance\lifecycle.js

Performance API

根据上述可知Performance能够获取到当前页面中的性能相关信息,是个丰富的API

暂列举一些常用的属性&方法,其余详见传送门

属性

Performance.timing

对象,包含了当前页面与性能相关的信息,输出可见:

微信截图_20210927215915.png

关于该对象各属性说明:PerformanceTiming

利用某些属性可进行常用计算:如 DNS查询耗时:domainLookupEnd - domainLookupStart等,见Performance — 前端性能监控利器

方法

Performance.mark(name)

根据name值,在浏览器的性能缓冲区创建一个时间戳

Performance.clearMarks(name)

name参数可选,若存在则根据值对应移除创建的时间戳,反之移除性能缓存区的所有时间戳标记

Performance.measure(name, startMark, endMark)

利用Performance.mark进行开始标记和结束标记,定义一次性能测量,命名为name值

performance.getEntriesByName(name,type)

根据测量名称name获取测量数据,返回一个给定名称和name和type属性的性能信息对象数组

Performance.clearMeasures(name)

同Performance.clearMarks,name也是可选,移除对应的某个测量或所有测量实体数据

Performance.now()

返回一个精确到毫秒的时间戳,可用于简易计算某代码段的执行时间

const start = window.performance.now();
// ……
const end = window.performance.now();
console.log(`执行了${end-start}ms`)

自定义封装

借鉴前端性能监控window.performance的巧妙写法一文

避免performance.timing某些属性,如loadEventEnd值在onload触发后仍然为0,利用setInterval进行如下封装:

import Vue from "vue";
const isServer = Vue.prototype.$isServer;

export default {
  methods: {
    // 获取页面性能数据
    getPerformanceInfo() {
      if (isServer) return;
      let perf = window.performance || window.webkitPerformance;
      if (!perf) return;

      let t = perf.timing;
      let info = {};

      // 设置定时器的作用:避免performance.timing.loadEventEnd为0
      let timer = setInterval(() => {
        if (t.loadEventEnd !== 0) {
          clearInterval(timer);
          // eg:DNS查询耗时
          info.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
          // 其他计算
          // ……
        }
      }, 100);

      return info;
    },
    // 进行时间戳标记
    perfMark(tag) {
      if (isServer) return;
      performance.mark(`${tag}`); // 添加标记名称,用于后续计算时间差
    },
    // 定义测量,名称的默认值action,后移除声明的时间戳和测量
    perfMeasure(startTag, endTag, name = "action") {
      if (isServer) return;
      performance.measure(`${name}`, `${startTag}`, `${endTag}`);
      let measures = performance.getEntriesByName(`${name}`);
      this.perfClear(name, startTag, endTag);
      return measures[0];
    },
    // 清除信息
    perfClear(name, ...tags) {
      // 移除指定声明的时间戳标记
      tags.forEach(item => {
        performance.clearMarks(`${item}`);
      });
      // 移除指定声明的测量
      if (name) performance.clearMeasures(`${name}`);
    },
    // 获取时间戳标记信息
    toGetPerformanceMark(name) {
      return performance.getEntries({name : name, entryType: "mark"}).filter(item=>item.name===name);
    }
  }
};

封装后使用:

import Performance from "./performance";
export default {
    // ……
    mixins: [Performance],
    data() {
        return {
            perfInfo: {}
        }
    },
    mounted() {
        let timing = this.getPerformanceInfo();
        // 设置定时器,确保数据返回
        let timer = setInterval(() => {
          if (Object.keys(timing).length) {
            clearInterval(timer);
            this.perfInfo = Object.assign({}, this.perfInfo, timing);
          }
        }, 100); 
    }
    // ……
    // perfMark、perfMeasure的使用,仅属于简单的封装,暂不列举,具体可参考MDN Web Docs的demo
}

注意事项

MDN Web Docs中,对于 Performance.timing 提及:

已废弃:  该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。

Last but not least

如有不妥,请多指教~