记一次页面卡顿排查
前述
前段时间上线的一个移动端的项目,由于开发时间仓促,一直被用户投诉页面卡顿。现在终于有时间来好好排查一下,看到底是什么原因。业务代码都不是自己写的,这是颇为头疼的问题。到了自己手上也只能努力的填坑了,伐开心。
chrome Timeline分析
首先当然是祭出开发神器--chrome,来看看页面的fps和js执行时间都是什么样子的。
本文不是介绍Timeline的知识,请还不了解的同学自行学习。传送门:https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/timeline-tool
果然fps是锯齿状的,顶上伴随着红条(可能存在性能问题的地方)。从图中可以发现,出现fps较低的地方,大都伴随着过多的script执行耗时(黄色部分)。接着我们选取一段fps较低的时间段,来看下具体有哪些event。
从图中可以看出,这个函数执行了118ms(当然页面卡顿还有别的地方引起,我就不一一描述出来了),而要确保页面不卡顿的时间是16.7ms。这显然要导致卡顿。这个函数内发生了很多事件。我发现了一个很糟糕的事,强制reflow,这可是前端性能的大忌了。而这个回流占用了大部分的函数执行时间。这个reflow是发生在“renderCommlist”函数内的,然后我点进这个函数看做了些什么操作。代码如下:
function renderCommlist(data, $dom) {
var prex = getMaidianPre();
totalCount = data.cnt - 0;
if (totalCount != 0) {
$("#palmrobtimes").show();
indexComm = fillCommListData(data, prex, indexComm);
var render = template.compile(document.getElementById("tmpl_pro_item").innerHTML);
var html = render(data);
renderCommlistAsyc(html, $dom);
$("#toTop").show()
} else {
commListEmpty($dom);
$("#toTop").hide()
}
dataLoading = false
}
根据执行的顺序我可以断定这个reflow发生对应的是“ $("#toTop").show()”这句。这就让我诧异了,一个zepto的show()方法竟然会导致这么严重的后果。我是长姿势了。而“#toTop”对应的DOM是这样的:
“toTop”是被一个fixed的标签包裹起来的。根据我的所学一个脱离文档流的dom不应该能造成这么严重的reflow啊。所以我们要来zepto的show到底做了一些什么事情。
接着我们来看Call Tree。看看show里面调用了哪些函数。
我发现,show里面调用了n,t.fn.animate,t.fn.anim等函数。主要发时间都消耗在了t.fn.anim上了。从函数名上看,这应该是动画相关的函数。我只是想要个显示元素,竟然调用了动画函数,不知道为什么。从“Layout”后面的链接点进去可以定位到触发“Layout”的,代码如下:
// trigger page reflow so new elements can animate
this.size() && this.get(0).clientLeft
这样我就明白了。就是这一句导致强制reflow。而我是要显示个元素,不要动画,这显然是没必要的。我翻看了下源代码,这里竟然还有注释。“为了新的元素能够执行动画,触发页面回流”。原来作者是故意的,不过显然低估了reflow的威力。其实也不是所有的场景会造成这么严重的回流耗时,只是我的场景比较“幸运”,在调用的这个函数的同时,程序往页面里append了DOM,放大了这个reflow。
对zepto的show函数的分析
至于show函数是怎么调用到animate的我还是比较好奇。在源码里查看了下。在zepto的核心模块“zepto.js”里其实show是这样的:
show: function(){
return this.each(function(){
this.style.display == "none" && (this.style.display = '')
if (getComputedStyle(this, '').getPropertyValue("display") == "none")
this.style.display = defaultDisplay(this.nodeName)
})
},
并没有去调用anim的。所以我就全局搜索了下“$.fn.show”发现这个函数是在“fx_methods.js”模块里的。
$.fn.show = function(speed, callback) {
origShow.call(this)
if (speed === undefined) speed = 0
else this.css('opacity', 0)
return anim(this, speed, 1, '1,1', callback)
}
这样就明白了,这个函数把上面的show给覆盖掉了,添加了动画。而anim又调用了“fx.js”里的“$.fn.animate”,这样就看到了上面的执行效果。不够我还是觉得动画没必要。其实“fx.js”和“fx_methods.js“,其实是可以不用打包到zepto里的,zepto的默认模块里也没这两个。
总结
这只是页面卡顿的一个点,当然还有很多,我们的页面卡顿就是由这样一个一个的点造成的。所以自己以后日常多多注意页面的性能。多用chrome dev来分析页面存在的性能问题。然后不要迷信开源框架,也是有缺陷的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。