在对Vue
项目进行性能优化时,除了借助外部工具之外,还可以利用框架自身的性能埋点来加以分析。在分析具体Vue
中性能埋点代码之前,先对监控前端性能的一些API
进行简单阐述。
一、Performance
Performance 接口可以获取到当前页面中与性能相关的信息,可以通过 window.performance
来引用。
有很多文章在介绍前端性能监控时,将performance
对象固化了,W3C组织提供了更为全面的性能分析矩阵,像performance.timing
这样强大的API
已经被废弃,现在的performance
接口是High Resolution Time API
的一部分,同时也融合了Performance Timeline API
、Navigation Timing API
、User Timing API
和Resource 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
中性能监测的实现是通过自己封装的mark
与measure
方法实现的,源码如下所示:
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)
}
}
}
复制代码
可以看到,mark
与measure
方法在生产环境下值为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')
}
/*...*/
}
复制代码
在模板编译之前,生成名为compile
的mark
类型实体。接着调用compileToFunctions
方法进行模板编译,完成模板编译后,首先生成名为compile end
的mark
类型实体,然后调用measure
方法生成名称为vue name compile
的measure
类型实体,其中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 render
、vue name render
,其中name
为组件名称。
三、总结
W3C性能小组引入Performance API
来进行页面性能分析,其中performance.mark()
方法用来添加一个时间戳实体,performance.measure()
方法使用一个实体对象记录两个时间戳之间信息。
全局配置Vue.config.performance
决定着是否在浏览器开发工具的性能/时间线面板中启用性能追踪。
Vue
在四个场景中提供了性能监测:组件初始化、模板编译、生成虚拟DOM、渲染真实DOM的patch过程。对应measure
类型实体的名称分别为:vue name init
、vue name compile
、vue name render
以及vue name render
,其中name
为组件名称。在浏览器开发工具的性能/时间线面板中的Timings
中可以查看具体信息。
欢迎关注公众号:前端桃花源,互相交流学习!