H5页面卡顿,到底是谁闯的祸

3,302 阅读5分钟

Android手机QQ在健康-积分商城中上下滑动兑换奖品列表,发现Web页面有卡顿、不流畅、用户体验差。这个案例的卡顿问题不止出现在低端机,在配置中高端的锤子与LG G3手机上,仍然卡顿较为严重。因此我们需要一些其他手段来调试、查询Web页面的性能,最常用的是使用Chrome+DevTools,下面简要介绍其功能与调试手段。

Chrome通过inspect是可以进行remoting Debug的,而且手机端所见即PC端所得,很方便。下面介绍一下调试手机的WebView页面方法。

evTools几个面板的主要功能如下。

Elements顾名思义查看页面元素,即各种DOM、CSS的属性。

Resources各种资源类,如字体、图片、DB及Session都可以在这里查看。

Network网络请求,可以查看页面所有的网络请求情况,查看对应的JS脚本、大小,延时及TimeLine等信息。

Sources执行脚本都可以在这里查看,并且支持代码格式化,断点调试,非常好用。

TimeLine可以分别按下面的条件查看执行的时间线、事件(Events)、帧(Frame)、内存(Memory)。

Profiles查看一段时间CPU执行情况,堆,内存分配情况。

所谓工欲善其事必先利其器,有了好的调试工具,便可开始分析了。

首先怀疑的是可能加载大图片的问题,通过Resources面板发现,并没有很大的图片,一般是10~30KB之间的PNG图片,还有几张10KB左右的Banner图片,而且全部加载完成,大概是有20多张图片,因此排除是大图片吃内存的怀疑。再怀疑页面是否有重复请求资源的情况,通过Network面板查看,也并没有太大问题,页面是按lazy-hold方式实现的,全部加载完所有积分商城的奖品之后,并没有额外的网络请求,网络情况的问题也可以排除在外。在没有办法确定问题原因的情况下,可以通过TimeLine查看是在哪里发生了卡顿,如图所示。

null

如图所示,方框内的时间轴上,就是出现卡顿的时间范围,在框内确实出现了大段的重绘操作(Recalculatestyle),换到事件(Events)面板,touchend事件,占据了很长的时间片,并且卡顿的时间区间也是在touchend事件的中间偏后的位置,下面就通过进到对应的JS脚本一探究竟。查看touchend事件对应的回调方法_end,只是一个停止操作,并没有其他操作,难道还有其他的事件影响这个函数执行吗?继续对其他事件也进行监听,发现确实有可疑的地方,页面间隔把webkitTransitionEnd事件抛过来。查看是哪个DOM抛过来的,发现原来是Banner定时轮播,会发一个webkitTransitionEnd消息,该消息会导致在滑动到最后时卡顿一下,TransitionEvent事件如图所示。

null

当在Elements面板中去掉Banner,再次滑动页面,整个过程基本就流畅了,看样子这就是症结,为什么两个或者多个事件会导致卡顿呢,仍然需要我们继续分析。由于页面之前使用的是iScroll组件,下面重点分析iScroll的touchend方法。touchend是这个控件的一个关键点与难点,把这个方法理解透彻了,基本可以理解整个iScroll事件机制。首先是初始化操作,在手指离开前做了状态保存。判断是否是点击事件(move是否为true),如果是的话,createEvent触发该事件;如果不是点击事件,则惯性移动到目标位置。首先还是初始化操作,这里面屏蔽了原生事件,如果处理不好,就会引发点击无效、白屏等问题,如图所示。

null

下面就是当手指离开屏幕的操作,如图所示。

null

duration是当前拖动的事件,这里不是手指触屏到离开,因为move时每300ms变更一次。记录当前x、y位置,记录当前移动位置distanceY,然后重置结束时间,这里有一个resetPosition方法,如图所示。

null

它记录是不是已经离开了边界,如果离开边界了就不会执行后面的逻辑,而直接重置DOM位置,这里还用到了我们的scrollTo方法,该方法尤其关键,如图所示。

null

这个方法是非常重要的方法,传入距离与时间后,就会移动到对应位置。这里用到了前文描述的settimeout实现动画方案,有一点需要回到Start部分重新思考,为什么CSS停止了动画?原因是transitionend事件。 transitionend事件会在CSS transition结束后触发。当transition完成前移除transition时,比如移除CSS的transition-property属性,事件将不会被触发,如图所示。

null

所以,第二次touchstart时候,便停止了动画,应该先取消动画再移动位置。如果没有超出边界,便滑动到应该去的位置。当然,用户可能只想点击而已,这个时候就要触发相关的点击事件。如果需要获取焦点,那么获取焦点即可。其实卡顿的原因基本明确了,就是Banner中的transactionend打断了动画。在整个DOM有两个需要处理的消息事件,结果就是感觉滑动很卡,并且不能很好地定位原因,其实导致这个原因的主要因素如下。

将事件绑定到了document上,而不是具体wrap的元素上,这样的话,就算另外一个元素隐藏了,滑动的时候实际上还是执行了两个逻辑,从而出现了卡顿现象,当然,如果Banner的元素不可见的话,应该释放其中的事件句柄,当时没有这么做。最后开发人员的解决方案是去掉iScroll,使用原生的滚动方法,后面已经验证确实可以解决卡顿问题了。

转自:wemedia.ifeng.com/54566459/we…