阅读 3894

前端性能优化之性能测试

前端性能优化是一个很宽泛的概念,有很多教程都有前端性能优化的方法,这也是我们一直在关注的一件重要事情。配合各种方式、手段、辅助系统,前端优化的最终目的都是提升用户体验,改善页面性能,我们常常竭尽全力进行前端页面优化,但却忽略了这样做的效果和意义。先不急于探究前端优化具体可以怎样去做,先看看什么是前端性能,应该怎样去了解和评价前端页面的性能。

通常前端性能可以认为是用户获取所需要页面数据或执行某个页面动作的一个实时性指标,一般以用户希望获取数据的操作到用户实际获得数据的时间间隔来衡量。例如用户希望获取数据的操作是打开某个页面,那么这个操作的前端性能就可以用该用户操作开始到屏幕展示页面内容给用户的这段时间间隔来评判。用户的等待延时可以分成两部分:可控 等待延时和 不可控 等待延时。可控 等待延时可以理解为能通过技术手段和优化来改进缩短时间的部分,例如减小图片大小让请求加载更快、减少 HTTP 请求数等。不可控 等待延时则是不能或很难通过前后端技术手段来改进优化的,例如鼠标点击延时、CPU 计算时间延时、ISP(Internet Service Provider,互联网服务提供商) 网络传输延时等。所以要知道的是,前端中的所有优化都是针对 可控等待延时 这部分来进行的,下面来了解一下如何获取和评价一个页面的具体性能。

前端性能测试

获取和衡量一个页面的性能,主要可以通过以下几个方面:Performance Timing APIPrpfile 工具、页面埋点计时、资源加载时序图分析。

Performance Timing API

Performance Timing API 是一个支持 Internet Explorer 9 以上版本及 WebKit 内核浏览器中用于记录页面加载和解析过程中关键时间点的机制,它可以详细记录每个页面资源从开始加载到解析完成这一过程中具体操作发生的时间点,这样根据开始和结束时间戳就可以计算出这个过程所花的时间了。

1-1W3C标准中 Performance Timing 资源加载和解析过程记录各个关键点的示意图,浏览器中加载和解析一个 HTML 文件的详细过程先后经历 unload、redirect、App Cache、DNS、TCP、Request、Response、Processing、onload 几个阶段,每个过程中开始和结束的关键时间戳浏览器已经使用 performance.timing 来记录了,所以根据这个记录并结合简单的计算,我们就可以得到页面中每个过程所消耗的时间。

Performance Timing API 关键时间点记录
                                                                   图1-1    Performance Timing API 关键时间点记录

function performanceTest(){
    let timing = performance.timing,
	readyStart = timing.fetchStart - timing.navigationStart,
	redirectTime = timing.redirectEnd - timing.redirectStart,
	appcacheTime = timing.domainLookupStart - timing.fetchStart,
	unloadEventTime = timing.unloadEventEnd - timing.unloadEventStart,
	lookupDomainTime = timing.domainLookupEnd - timing.domainLookupStart,
	connectTime = timing.connectEnd - timing.connectStart,
	requestTime = timing.responseEnd - timing.requestStart,
	initDomTreeTime = timing.domInteractive - timing.responseEnd,
	domReadyTime = timing.domComplete - timing.domInteractive,
	loadEventTime = timing.loadEventEnd - timing.loadEventStart,
	loadTime = timing.loadEventEnd - timing.navigationStart;
			
    console.log('准备新页面时间耗时:'+readyStart);
    console.log('redirect 重定向耗时:'+redirectTime);
    console.log('Appcache 耗时'+appcacheTime);
    console.log('unload 前文档耗时:'+unloadEventTime);
    console.log('DNS 查询耗时:'+lookupDomainTime);
    console.log('TCP 连接耗时:'+connectTime);
    console.log('request 请求耗时:'+requestTime);
    console.log('请求完毕至DOM加载:'+initDomTreeTime);
    console.log('解析DOM树耗时:'+domReadyTime);
    console.log('Load事件耗时:'+loadEventTime);
    console.log('加载时间耗时:'+loadTime);
}
复制代码

通过上面的时间戳计算可以得到几个关键步骤所消耗的时间,对前端有意义的几个过程主要是解析 DOM 树耗时、load 事件耗时和整个加载过程耗时等,不过在页面性能获取时我们可以尽量获取更详细的数据信息,以供后面分析。除了资源加载解析的关键点计时,preformance 还提供了一些其他方面的功能,我们可以根据具体需要进行选择使用。

performance.memory      //内存占用的具体数据
performance.now()       //performance.now()方法返回当前网页自performance.timing到现在的时间,可以精确到微秒,用于更加精确的计数。但实际上,目前网页性能通过毫秒来计算就足够了
performance.getEntries()        //获取页面所有加载资源的performance timing 情况。浏览器获取网页时,会对网页中每一个对象(脚本文件、样式表、图片文件等)发出一个HTTP请求。performance.getEntries 方法以数组形式返回所有请求的时间统计信息
performance.navigation      //performance 还可以提供用户行为信息,例如网络请求的类型和重定向次数等,一般都存放在performance.navigation对象里面
performance.navigation.redirectCount        //记录当前网页重定向跳转的次数
复制代码

Profile 工具

Performance Timing API 描述了页面资源从加载到解析各个阶段的执行关键点时间记录,但是无法统计 JavaScript 执行过程中系统资源的占用情况。ProfileChromeFirefox 等标准浏览器提供的一种用于测试页面脚本运行时系统内存和 CPU 资源占用情况的 API,以 Chrome 浏览器为例,结合 Profile,可以实现以下几个功能。

      1.分析页面脚本执行过程中最耗资源的操作

      2.记录页面脚本执行过程中 JavaScript 对象消耗的内存与堆栈的使用情况

      3.检测页面脚本执行过程中 CPU 占用情况

使用 console.profile()console.profileEnd() 就可以分析中间一段代码执行时系统的内存或 CPU 资源的消耗情况,然后配合浏览器的 Profile 查看比较消耗系统内存或 CPU 资源的操作,这样就可以有针对性的进行优化了。

console.profile();
//TODOS,需要测试的页面逻辑动作
for(let i = 0; i < 100000; i ++){
    console.log(i * i);
}
console.profileEnd();
复制代码

页面埋点计时

使用Profile可以在一定程度上帮助我们分析页面的性能,但缺点是不够灵活。实际项目中,我们不会多关注页面的内存或CPU资源的消耗情况,因为JavaScript有自动内存回收机制。我们关注更多的是页面脚本逻辑执行的时间。除了Performance Timing 的关键过程耗时计算,我们还希望检测代码的具体解析或执行时间,这就不能写很多的console.profile()console.profileEnd()来逐段实现,为了更加简单地处理这种情况,往往选择通过脚本埋点计时的方式来统计没部分代码的运行时间。

页面 JavaScript 埋点计时比较容易实现,和 Performance Timing 记录时间戳有点类似,我们可以记录 JavaScript 代码开始执行的时间戳,后面在需要记录的地方埋点记录结束时的时间戳,最后通过差值来计算一段 HTML 解析或 JavaScript 解析执行的时间。为了方便操作,可以将某个操作开始和结束的时间戳记录到一个数组中,然后分析数组之间的间隔就得到每个步骤的执行时间,下面来看一个时间点记录和分析的例子。

let timeList = [];
function addTime(tag) {timeList.push({"tag":tag,"time":+new Date()});}

addTime("loading");

timeList.push({"tag":"load","time":+new Date()});
//TODOS,load加载时的操作
timeList.push({"tag":"load","time":+new Date()});

timeList.push({"tag":"process","time":+new Date()});
//TODOS,process处理时的操作
timeList.push({"tag":"process","time":+new Date()});

console.log(parseTime(timeList));	//输出{load:时间毫秒数,process: 时间毫秒数}

function parseTime(time) {
    let timeStep={},
	endTime;
	for(let i = 0,len = time.length; i < len; i ++){
	    if(!time[i]) continue;

		endTime = {};
		for(let j = i+1; j < len; j++){
		    if(time[j] && time[i].tag == time[j].tag){
			    endTime.tag = time[j].tag;
			    endTime.time = time[j].time;
			    time[j] = null;
			}
		}

		if(endTime.time >= 0 && endTime.tag){
		    timeStep[endTime.tag] = endTime.time - time[i].time;
		}
	}
	return timeStep;
}
复制代码

这种方式常常在移动端页面中使用,因为移动端浏览器 HTML 解析和 JavaScript 执行相对较慢,通常为了性能优化,我们需要找到页面中执行 JavaScript 耗时的操作,如果将关键 JavaScript 的执行过程进行埋点计时并上报,就可以轻松找出 JavaScript 执行慢的地方,并有针对性地进行优化。

资源加载时序图

我们还可以借助浏览器或其他工具的资源加载时序图来帮助分析页面资源加载过程中的性能问题。这种方法可以粗粒度地宏观分析浏览器的所有资源文件请求耗时和文件加载顺序情况,如保证 CSS 和数据请求等关键资源优先加载,JavaScript 文件和页面中非关键性图片等内容延后加载。如果因为某个资源的加载十分耗时而阻塞了页面的内容展示,那就要着重考虑。所以,我们需要通过资源加载时序图来辅助分析页面上资源加载顺序的问题。

1-2 为使用 Fiddler 抓取浏览器访问地址时的资源加载时序图,图 1-3Chrome 浏览器访问猫眼电影官网时的资源加载时序图.根据资源加载时序图我们可以很直观地看到页面上各个资源加载过程所需要的时间和先后顺序,有利于找出加载过程中比较耗时的文件资源,帮助我们有针对性地进行优化。

                                                                   图1-2    Fiddler 页面加载文件资源时序图

                                                        图1-3    Chrome浏览器 猫眼电影官网页面加载文件资源时序图

总结

通过上文介绍的利用Performance Timing APIPrpfile工具、页面埋点计时、资源加载时序图分析几个方面来获取和衡量一个页面的性能,找出加载过程中比较耗时的文件资源,帮助我们有针对性地进行优化。医病必须诊断找到病因,前端性能测试在前端性能优化中属于诊断工作,只有找到病因,才能进行针对性治疗优化。在此欢迎大家加入QQ前端技术交流群 544587175 ,在后续我会从桌面浏览器和移动端浏览器两个方面详细讲解不同终端的优化策略,敬请关注......

关注下面的标签,发现更多相似文章
评论