今天看啥  ›  专栏  ›  白马笑西风

Vue2.0源码阅读笔记(十三):性能分析

白马笑西风  · 掘金  ·  · 2020-04-07 08:35
阅读 53

Vue2.0源码阅读笔记(十三):性能分析

  在对Vue项目进行性能优化时,除了借助外部工具之外,还可以利用框架自身的性能埋点来加以分析。在分析具体Vue中性能埋点代码之前,先对监控前端性能的一些API进行简单阐述。

一、Performance

  Performance 接口可以获取到当前页面中与性能相关的信息,可以通过 window.performance 来引用。
  有很多文章在介绍前端性能监控时,将performance对象固化了,W3C组织提供了更为全面的性能分析矩阵,像performance.timing这样强大的API已经被废弃,现在的performance接口是High Resolution Time API的一部分,同时也融合了Performance Timeline APINavigation Timing APIUser Timing APIResource Timing API

  关于performance对象的知识点很多,下面仅分析Vue性能埋点中使用的部分。

1、performance.mark

  performance.mark()方法在浏览器的性能缓冲区中使用给定名称添加一个timestamp(时间戳)实体,定义的时间戳实体可以通过getEntries(),getEntriesByName()getEntriesByType()等方法获取。

performance.mark("monkey")

const monkeyEntries = performance.getEntriesByName("monkey")
console.log(monkeyEntries[0])
/*
  输出内容为:
  {
    name: "monkey"
    entryType: "mark"
    startTime: 18.25500000268221
    duration: 0
  }
*/
复制代码

  时间戳实体对象的属性含义如下所示:

1、name:实体的名字。
2、entryType:实体类型,使用mark()方法创建的实体类型为"mark"。
3、startTime:创建时的时间戳。
4、duration:该事件的耗时。

  performance.clearMarks()方法用来移除浏览器性能缓存区的mark类型实体。如果调用该方法时没有传递参数,则移除所有的mark类型实体。

2、performance.measure

  performance.measure()方法在浏览器性能记录缓存中创建了一个名为时间戳的实体来记录两个特殊标志位(通常称为开始标志和结束标志)。通俗来说,performance.measure()方法使用一个实体对象记录两个时间戳之间信息。
  performance.measure()方法接受三个参数:测量实体的名字、开始测量标志名字、结束测量标志名字。measure类型的实体同样可以通过getEntries(),getEntriesByName()getEntriesByType()等方法获取。

performance.mark("start")
performance.mark("end")
performance.measure("myMeasure","start","end")
const measure = performance.getEntriesByName("myMeasure")
console.log(measure)
/*
  输出内容为:
  {
    name: "myMeasure"
    entryType: "measure"
    startTime: 21.25499999965541
    duration: 0.15499998698942363
  }
*/
复制代码

  可以看到measure类型的entryType属性值为measure,这点与mark类型实体不同。
  另外使用浏览器控制台做性能分析时可以看到measure类型实体部分,如下图所示:

  performance.clearMeasures()方法用来移除浏览器性能缓存区的measure类型实体。如果调用该方法时没有传递参数,则移除所有的measure类型实体。

二、vue中的性能监测

  Vue中性能监测的实现是通过自己封装的markmeasure方法实现的,源码如下所示:

export let mark
export let measure

if (process.env.NODE_ENV !== 'production') {
  const perf = inBrowser && window.performance
  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)
    }
  }
}
复制代码

  可以看到,markmeasure方法在生产环境下值为undefined,也就是说这些性能监测只能用于非生产环境。
  mark方法就是直接对window.performance.mark()方法的调用,measure方法在调用window.performance.measure()后,接着调用window.performance.clearMarks()方法清除mark类型实体。
  Vue在四个方面能够使用性能监测:

1、组件初始化
2、模板编译
3、生成虚拟DOM
4、渲染真实DOM的patch过程

1、组件初始化

  在开启性能追踪之前,首先检测全局配置Vue.config.performance是否为true,该配置决定是否在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。

Vue.prototype._init = function (options) {
  /*...*/
  let startTag, endTag

  if(process.env.NODE_ENV!=='production' && config.performance && mark){
    startTag = `vue-perf-start:${vm._uid}`
    endTag = `vue-perf-end:${vm._uid}`
    mark(startTag)
  }

  /*合并选项、初始化生命周期、初始化事件、初始化事件等*/

  if(process.env.NODE_ENV!=='production' && config.performance && mark){
    vm._name = formatComponentName(vm, false)
    mark(endTag)
    measure(`vue ${vm._name} init`, startTag, endTag)
  }
  /*...*/
}
复制代码

  在组件初始化开始时,添加开始标志的mark类型实体,该实体名称由字符串vue-perf-start:与组件uid拼凑而成。
  在经过合并选项、初始化生命周期、初始化事件、初始化事件等组件初始化操作完成后添加结束标志的mark类型实体,该实体名称由字符串vue-perf-end:与组件uid拼凑而成。
  最后调用measure方法生成measure类型的实体,该实体的名称为vue name init,其中name为组件名称。这样在浏览器开发工具的性能/时间线面板中的Timings中就能看到名为vue name init的相关信息。

2、模板编译

  在调用$mount方法完成挂载时,如果是完整版的Vue,则会在该方法中将模板字符串编译成渲染函数。

Vue.prototype.$mount = function(el,hydrating){
  /*...*/
  if(process.env.NODE_ENV!=='production' && config.performance && mark){
    mark('compile')
  }

  /*模板编译*/

  if(process.env.NODE_ENV!=='production' && config.performance && mark){
    mark('compile end')
    measure(`vue ${this._name} compile`, 'compile', 'compile end')
  }
  /*...*/
}
复制代码

  在模板编译之前,生成名为compilemark类型实体。接着调用compileToFunctions方法进行模板编译,完成模板编译后,首先生成名为compile endmark类型实体,然后调用measure方法生成名称为vue name compilemeasure类型实体,其中name为组件名称。

3、生成VNode与patch过程

  在挂载的过程中会调用mountComponent方法,其中将渲染函数转化成VNode的过程由_render方法完成,根据VNode生成真实DOM并插入DOM树的过程由_update方法完成。

export function mountComponent (vm,el,hydrating){
  /*...*/
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  }
  /*...*/
}
复制代码

  生成VNode与patch过程性能分析的measure类型实体名称分别为:vue name rendervue name render,其中name为组件名称。

三、总结

  W3C性能小组引入Performance API来进行页面性能分析,其中performance.mark()方法用来添加一个时间戳实体,performance.measure()方法使用一个实体对象记录两个时间戳之间信息。
  全局配置Vue.config.performance决定着是否在浏览器开发工具的性能/时间线面板中启用性能追踪。
  Vue在四个场景中提供了性能监测:组件初始化、模板编译、生成虚拟DOM、渲染真实DOM的patch过程。对应measure类型实体的名称分别为:vue name initvue name compilevue name render以及vue name render,其中name为组件名称。在浏览器开发工具的性能/时间线面板中的Timings中可以查看具体信息。

欢迎关注公众号:前端桃花源,互相交流学习!




原文地址:访问原文地址
快照地址: 访问文章快照