使用Chrome Timeline来优化页面性能

简介:

有时候,我们就是会不由自主地写出一些低效的代码,严重影响页面运行的效率。或者我们接手的项目中,前人写出来的代码千奇百怪,比如为了一个 Canvas 特效需要同时绘制 600 个三角形,又比如 Coding.net 的任务中心需要同时 watch 上万个变量的变化等等。那么,如果我们遇到了一个比较低效的页面,应该如何去优化它呢?

优化前的准备:知己知彼

在一切开始之前,我们先打开 F12 面板,熟悉一下我们接下来要用到的工具:Timeline:

嗯没错就是它。下面逐一介绍一下吧。区域 1 是一个缩略图,可以看到除了时间轴以外被上下分成了四块,分别代表 FPS、CPU 时间、网络通信时间、堆栈占用;这个缩略图可以横向缩放,白色区域是下面可以看到的时间段(灰色当然是不可见的啦)。区域 2 可以看一些交互事件,例如你滚动了一下页面,那么这里会出现一个 scroll 的线段,线段覆盖的范围就是滚动经过的时间。区域 3 则是具体的事件列表了。

一开始没有记录的时候,所有的区域都是空的。开始统计和结束统计都很简单,左上角那坨黑色的圆圈就是。它右边那个长得像“禁止通行”的按钮是用来清除现有记录的。当有数据的时候,我们把鼠标滚轮向上滚,可以看到区域被放大了:

短短的时间里,浏览器做了这么多事情。对于一般的屏幕,原则上来说一秒要往屏幕上绘制 60 帧,所以理论上讲我们一帧内的计算时间不能超过 16 毫秒,然而浏览器除了执行我们的代码以外,还要干点别的(例如计算 CSS,播放音频……),所以其实我们能用的只有 10~12 毫秒左右。

差不多熟悉操作了,那么就来一下实战吧!假如有一天,你接手了这样一段代码:


 
 
  1. <!-- 一段小动画:点击按钮之后会有一个爆炸的粒子效果 --> 
  2. <!DOCTYPE html> 
  3. <html> 
  4. <head> 
  5.     <meta charset="utf-8"
  6.     <title>Test</title> 
  7.     <style> 
  8.         .main { 
  9.             position: relative
  10.             width: 500px; 
  11.             height: 500px; 
  12.             background: #000; 
  13.             overflow: hidden; 
  14.         } 
  15.         .circle { 
  16.             position: absolute
  17.             border-radius: 50%; 
  18.             border: 1px solid #FFF; 
  19.             width: 8px; 
  20.             height: 8px; 
  21.         } 
  22.     </style> 
  23. </head> 
  24. <body> 
  25.     <div class="main"></div> 
  26.     <hr> 
  27.     <button onclick="showAnimation()">点我</button> 
  28.     <script src="jquery.min.js"></script> 
  29.     <script src="animation.js"></script> 
  30. </body> 
  31. </html> 

 
 
  1. // animation.js  
  2.   
  3. // 粒子总数  
  4. var COUNT = 500;  
  5. // 重力  
  6. var G = -0.1;  
  7. // 摩擦力  
  8. var F = -0.04;  
  9.   
  10. function init() {  
  11.     for (var i = 0; i < COUNT; i++) {  
  12.         var d = Math.random() * 2 * Math.PI;  
  13.         var v = Math.random() * 5;  
  14.         var circle = $('<div id="circle-' + i + '" class="circle" data-x="250" data-y="250" data-d="' + d + '" data-v="' + v + '"></div>');  
  15.         circle.appendTo($('.main'));  
  16.     }  
  17. }  
  18.   
  19. function updateCircle() {  
  20.     for (var i = 0; i < COUNT; i++) {  
  21.         var x = parseFloat($('#circle-' + i).attr('data-x'));  
  22.         var y = parseFloat($('#circle-' + i).attr('data-y'));  
  23.         var d = parseFloat($('#circle-' + i).attr('data-d'));  
  24.         var v = parseFloat($('#circle-' + i).attr('data-v'));  
  25.         var vx = v * Math.cos(d);  
  26.         var vy = v * Math.sin(d);  
  27.         if (Math.abs(vx) < 1e-9) vx = 0;  
  28.         // 速度分量改变  
  29.         vx += F * Math.cos(d);  
  30.         vy += F * Math.sin(d) + G;  
  31.         // 计算新速度  
  32.         v = Math.sqrt(vx * vx + vy * vy);  
  33.         if (vy > 0) d = Math.acos(vx / v);  
  34.         else d = -Math.acos(vx / v);  
  35.         // 位移分量改变  
  36.         x += vx;  
  37.         y += vy;  
  38.         $('#circle-' + i).attr('data-x', x);  
  39.         $('#circle-' + i).attr('data-y', y);  
  40.         $('#circle-' + i).attr('data-d', d);  
  41.         $('#circle-' + i).attr('data-v', v);  
  42.         $('#circle-' + i).css({'top': 400 - y, 'left': x});  
  43.     }  
  44. }  
  45.   
  46. var interval = null;  
  47.   
  48. function showAnimation() {  
  49.     if (interval) clearInterval(interval);  
  50.     $('.main').html('');  
  51.     init();  
  52.     interval = setInterval(updateCircle, 1000 / 60);  
  53. }  

效果如下(右上角的 FPS 计数器是 Chrome 调试工具自带的):

只有 10 FPS……10 FPS……坑爹呢这是!

好吧,打开 Timeline,按下记录按钮,点一下页面中的“点我”,稍微过一会儿停止记录,就会得到一些数据。放大一些,对 jQuery 比较熟悉的同学可以看出来,这些大部分是 jQuery 的函数。我们点一下那个 updateCircle 的区块,然后看下面:

这里告诉我们,这个函数运行了多久、函数代码在哪儿。我们点一下那个链接,于是就跳到了 Source 页:

是不是很震撼,之前这个页面只是用来 Debug 的,没想到现在居然带了精确到行的运行时间统计。当然,这个时间是当前这一行在“刚才我们点击的区块对应的执行时间段”中运行的时间。所以我们就拿最慢的几句话来下手吧!

优化一:减少 DOM 操作

看到这几行代码,第一反应是:mdzz。本来 DOM 操作就慢,还要在字符串和 float 之间转来转去。果断改掉!于是用一个单独的数组来存 x、y、d、v 这些属性。


 
 
  1. var objects = []; 
  2. // 在 init 函数中 
  3. objects.push({ 
  4.     x: 250, 
  5.     y: 250, 
  6.     d: d, 
  7.     v: v 
  8. }); 
  9. // 在 updateCircle 函数中 
  10. var x = objects[i].x; 
  11. var y = objects[i].y; 
  12. var d = objects[i].d; 
  13. var v = objects[i].v; 
  14. // …. 
  15. objects[i].x = x; 
  16. objects[i].y = y; 
  17. objects[i].d = d; 
  18. objects[i].v = v; 

效果显著!我们再来看一下精确到行的数据:

优化二:减少不必要的运算

所以最耗时的那句话已经变成了计算 vx 和 vy,毕竟三角函数算法比较复杂嘛,可以理解。至于后面的三角函数为什么那么快,我猜可能是 Chrome 的 V8 引擎将其缓存了(这句话不保证正确性)。然而不知道大家有没有发现,其实计算 d 完全没必要!我们只需要存 vx 和 vy 即可,不需要存 v 和 d!


 
 
  1. // init 
  2. var vx = v * Math.cos(d); 
  3. var vy = v * Math.sin(d); 
  4. objects.push({ 
  5.     x: 250, 
  6.     y: 250, 
  7.     vx: vx, 
  8.     vy: vy 
  9. }); 
  10. // updateCircle 
  11. var vx = objects[i].vx; 
  12. var vy = objects[i].vy; 
  13. // 计算新速度 
  14. var v = Math.sqrt(vx * vx + vy * vy); 
  15. if (Math.abs(vx) < 1e-9) vx = 0; 
  16. // 速度分量改变 
  17. vx += F * vx / v; 
  18. vy += F * vy / v + G; 
  19. // …. 
  20. objects[i].vx = vx; 
  21. objects[i].vy = vy; 

只有加减乘除和开平方运算,每次比原来的时间又少了两毫秒。从流畅的角度来说其实已经可以满帧运行了,然而为什么我还是觉得偶尔会有点卡呢?

优化三:替换 setInterval

既然偶尔会掉帧,那么就看看是怎么掉的呗~原则上来说,在每一次浏览器进行绘制之前,Timeline 里面应该有一个叫 Paint 的事件,就像这样:

看到这些绿色的东西了没?就是它们!看上面的时间轴,虽然代码中 setInterval 的长度是 1000/16 毫秒,但是其实根本不能保证!所以我们需要使用 requestAnimationFrame 来代替它。这是浏览器自带的专门为动画服务的函数,浏览器会自动优化这个函数的调用时机。并且如果页面被隐藏,浏览器还会自动暂停调用,有效地减少了 CPU 的开销。


 
 
  1. // 在 updateCircle 最后加一句 
  2. requestAnimationFrame(updateCircle); 
  3. // 去掉全部跟 setInterval 有关的句子,把 showAnimation 最后一句直接改成这个 
  4. updateCircle(); 

我们至少可以保证,我们每算一次,屏幕上就会显示一次,因此不会掉帧(前提是每计算一次的时间小于 12ms)。但是虽然计算时间少了,浏览器重计算样式、绘制图像的时间可是一点都没变。能不能再做优化呢?

优化四:使用硬件加速、避免反复查找元素

如果我们用 transform 来代替 left 和 top 来对元素进行定位,那么浏览器会为这个元素单独创立一个合成层,专门使用 GPU 进行渲染,这样可以把重计算的代价降到最低。有兴趣的同学可以研究一下“CSS 硬件加速”的机制。同时,我们可以缓存一下 jQuery 的元素(或者 DOM 元素),这样不用每次都重新查找,也能稍微提高一点效率。如果把元素缓存在 objects 数组中,那么连 id 都不用写了!


 
 
  1. // init 
  2. var circle = $('<div class="circle"></div>'); 
  3. objects.push({ 
  4.     x: 250, 
  5.     y: 250, 
  6.     vx: vx, 
  7.     vy: vy, 
  8.     // 其实可以只存 DOM,不存 jQuery 对象 
  9.     circle: circle[0] 
  10. }); 
  11. // updateCircle 里面 for 循环的最后一句话替换掉 
  12. objects[i].circle.style.transform = 'translate(' + x + 'px, ' + (400 - y) + 'px)';  

看起来是不是很爽了?

其实,优化是无止境的,例如我在 init 函数中完全可以不用 jQuery,改用 createDocumentFragment 来拼接元素,这样初始化的时间就可以急剧缩短;调换 updateCircle 中的几个语句的顺序,在 V8 引擎下效率可能会有一定的提升;甚至还可以结合 Profile 面板来分析内存占用,查看浏览器绘图的细节……然而个人感觉并用不到这么极限的优化。对于一个项目来说,如果单纯为了优化而写一些奇怪的代码,是很不合算的。


作者:Coding_net

来源:51CTO

相关文章
|
6月前
|
Web App开发 JavaScript
使用CRXjs、Vite、Vue 开发 Chrome 多页面插件,手动配置 vite.config.ts 和 manifest.json 文件
使用CRXjs、Vite、Vue 开发 Chrome 多页面插件,手动配置 vite.config.ts 和 manifest.json 文件
232 0
|
7月前
|
Web App开发 SQL 前端开发
性能工具之前端分析工Chrome Developer Tools性能标签
【2月更文挑战第22天】性能工具之前端分析工Chrome Developer Tools性能标签
76 1
性能工具之前端分析工Chrome Developer Tools性能标签
|
7月前
|
Web App开发 Windows
Windows【Chrome浏览器 01】首次安装的谷歌Chrome浏览器出现无法打开此页面问题处理(详细图文步骤)
Windows【Chrome浏览器 01】首次安装的谷歌Chrome浏览器出现无法打开此页面问题处理(详细图文步骤)
142 0
|
Web App开发
Chrome浏览器所有页面崩溃
Chrome浏览器所有页面崩溃
243 0
|
Web App开发 JSON 监控
Chrome浏览器扩展开发之自动化操作页面
Chrome浏览器扩展开发之自动化操作页面
463 0
|
Web App开发 存储 前端开发
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
407 0
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
|
Web App开发 SQL JavaScript
【性能】7分钟带你了解【尤大】都在使用的 Chrome Runtime Performance Debug!
前段时间用Vue3开发了一个线上标准的项目,也都已经提测了,最近可以抽时间来整理一下项目中用到的技术栈,( 我用前端【最新】技术栈完成了一个生产标准的项目【Vue3 + TS + Vite + Pinia + Windicss + NavieUI】。 在开发之前特地重温了一下18年尤大的VUE CONF(杭州),过程中发现尤大使用到了这个Performance去分析JavaScript每一帧的计算,之前知道这个工具,但是平时基本不怎么用,借此机会学一下并做一些记录。
227 0
|
Web App开发 缓存 异构计算
猿大师播放器在Chrome网页播放多路H.265海康威视RTSP视频流页面卡顿CPU占用高怎么办?
试用猿大师播放器播放一路视频效果很不错,延迟可以控制在200毫秒左右,但是如果播放多路高清视频,CPU占用就会比较高,并且网页也会卡顿,该如何解决呢?
610 0
猿大师播放器在Chrome网页播放多路H.265海康威视RTSP视频流页面卡顿CPU占用高怎么办?
chrome18-使用network waterfall分析页面载入性
chrome18-使用network waterfall分析页面载入性
313 0
chrome18-使用network waterfall分析页面载入性
|
Web App开发 前端开发 JavaScript
Chrome 87 新特性解读,多年来 Chrome 性能最大提升!
今天 Chrome 更新了 87 最新版,这是今年最后一次 Chrome 更新了,这个版本是多年来 Chrome 性能获得最大提升的一次,开发者工具也进行了大幅度更新。 有用户认为,原本 Chrome 的性能问题诟病已久,然而在新 Edge 出来了以后,性能突然就提升了,这显然是谷歌方面受到了 Edge 市场份额暴增带来的压力。 另外,在 Mac 上的 Chrome 还更新了图标,这应该是为了适配 Big Sur 专门进行的设计。
Chrome 87 新特性解读,多年来 Chrome 性能最大提升!