2024/06/10
Performance是Chrome浏览器自带的性能监测工具。根据我的使用,简单理解就是我们可以通过它录制一段时间的浏览器活动,通过活动的数据去分析页面是否存在提升的空间。想要获取页面的活动数据,那我们的第一步便是录制浏览器的活动。
打开F12,进入Perfomance标签页,我们可以看到出现了提示。我们不想看这个文本在说什么,我们只想马上开始用(不是),由于我已经百度翻译过了(不是,看过了),所以文本提示是在说箭头所指的地方,实心圆代表开始录制,而刷新图标表示它可以帮你刷新并录制从点击页面刷新到页面加载的整个过程。当我们想看首屏加载速度时,我们可以点这个刷新图标,当我们想为所欲为的时候,我们可以使用实心圆代表的录制按钮。
那么接下来的问题是,要录制什么,我知道你们的答案:“先随便录录看看”。但是随便录一段,可能只会让你知道这个Performance面板都有什么,一顿操作之后很快就会索然无味。根据我的一个方法论,找一个或者创造一个有性能问题的页面,使用Performance查看其中的性能瓶颈,有助于我们的理解。
幸运的是,我在官方网站关于Performance的介绍中,找到了一个这样的demo。这个demo需要科学上网才能访问,但是我又非常有幸的拜读了一位道友的Performance指北,他的仓库中的demo2正是官方的以前的demo(运行起来也差不多)。
在正式录制之前,我们知道我们处于PC的状态,而一般手机的CPU往往没有PC上的强大,如果我们要进行手机端的性能评估,使用PC的CPU状态往往会得到更优秀的结果而影响性能判断,Perfomance面板上提供了网速和CPU的限制以便我们模拟手机状态。下图中齿轮形状可以展开网络、CPU和其他设置,为了能更快的模拟出动画的卡顿,这里可以把CPU设置为4倍慢速。
在完成设置之后,接下来便是我们的录制操作了
下图便是我们生成的报告
在完成浏览器渲染这件事上,浏览器分出了不同的进程和线程协同完成,比如合成线程Compositor负责把主线程提交的绘制Painting列表合成并,这部分涉及浏览器的工作原理,这里简单了解下即可。
接下来,我们需要查看某段时间具体的评估数据,我们可以通过鼠标滚轮或者点击或拖拽概览面板的方式查看特定的时间段,为了查看优化前是哪里的性能出现了问题,我把查看概览面板缩小成只能看到执行了3个任务的大小。
单个任务从上往下为调用关系,Anemotion Frame Fired 调用了Function Call , 而Function Call 调用了app.update,我们管Main展开看到的图叫火焰图 ,只是火焰苗是朝下的,因为随着调用栈越来越深,每个任务将会越分越细,从而形成上宽下窄的倒火焰形状。
继续缩小查看范围,直到我们只看到一个任务执行。
在面板中我们可以点击某一个色块查看详情,跟随调用栈的足迹,我们找到位于火焰顶部的Js色块即色块,查看图中蓝色区域的详情,这里显示执行自身Js花费了23ms,接下来对Js调用的rendering花费了24ms,从图中可以看到在色块有无数的紫色小方块,而等待这些紫色小方块的执行延长了整个Js的执行时间。
调整查看范围,直到看到优化后的具体几个任务。
我们可以看到优化后的面板app.update色块所有的紫色小方块都消失了,而紫色小方块是跟布局有关的活动,看到这里我们可以锁定优化前的js代码中存在改变布局的操作,毋庸置疑,在移动蓝色小方块时,必然会导致布局的变化,而优化前后,到底是什么操作导致优化前在执行js时进行了反复的布局计算,而优化后只在js执行后才执行一次布局计算并更新页面呢?看来我们得进入代码层面继续我们的调查了。
接下来我们通过一下步骤进入到具体的代码查看两者的区别
是一段更新蓝色小方块位置的代码,使用了来表示在执行动画过程中需要重新绘制页面时调用,这个方法也是执行动画用于代替定时器常用的优化手段,可以避免我们在浏览器不进行更新页面时执行了动画的相关代码。
在这个界面中,我们可以看到最右边除了有代码所在的行数,还有代码执行某一行需要多长的时间,黄色越深执行时间越长。可以看到优化前后执行时间最大的区别在于红色方块框住的两行代码,在这两行代码中,上一行表示设置当前蓝色小方块的top值以达到移动蓝色小方块的效果,后面一行代码则判断设置完top值之后判断蓝色小方块是否触顶以安排接下来的运动方向是向上还是向下运动。
优化前的代码在改变完蓝色方块的位置后,立刻又重新使用获取了当前小方块的位置,这时候浏览器不得不对页面进行重排来获取当前蓝色方块的位置,我们称之为强制同步布局。而在循环中不断获取则会引起布局抖动,即之前我们在色块下看到的大量紫色布局小方块。而使用以及变量确定高度不会触发同步布局,前者获取的是元素属性中的值,后者是缓存的值变量。
在前面的代码优化中,我们认识了强制同步布局导致性能瓶颈,但不论是优化前还是优化后,由于改变了元素的位置信息,浏览器进行渲染时都会进行完整的渲染流水线,而完整的渲染流水线大致如下图,从执行Js -> 生成样式 -> 计算布局 -> 绘制 -> 合成 -> 展示,我们称执行了完成流水线的行为为重排。
而有一些Js执行的改变样式并不会几何位置,比如更改透明度、背景颜色等。这时候渲染流水线上的计算布局便不会执行,我们管这种渲染过程称之为重绘。由于减少了计算布局的操作,重绘的效率比重排更高。但在这个案例中,改变几何位置是必然,我们不能通过优化成重绘来优化性能。那还有别的方法吗?有,使用CSS动画可以帮助我们进一步优化性能。
使用CSS的动画,一些不触发几何位置变化的动画可以完全跳过执行Js、生成样式、计算布局、绘制这些步骤,直接进入合成阶段。而合成阶段是在合成线程中执行的,并不会阻塞主线程,所以有时候我们能发现网页虽然卡死了,但是动画却依然在运行的现象。
动画可以把执行动画的部分单独划分成一层,你可以想象成ps中的图层,而这一层的动画将由合成线程接管,把这一层的每一帧的动画合成到主图层中,就完成了动画的效果。这就是为什么使用CSS执行动画是最好的选择。
但是有时候我们为了执行比较复杂的动画,不得不动用Js执行,这时候我们可以通过告诉浏览器,这部分的内容需要单独提到一层,同时浏览器会把这部分的变换通过合成线程处理,但是任何事情都有两面,过多的使用创建了过量的层,将导致浏览器占用的内存上升,所以我们要恰当的使用,以下代码告诉浏览box类需要进行几何变换以及透明度变化。