使用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

相关文章
|
数据采集 Web App开发 调度
Headless Chrome 优化:减少内存占用与提速技巧
在数据驱动的时代,爬虫技术至关重要。本文聚焦 Headless Chrome 优化方案,解决传统爬虫内存占用高、效率低等问题。通过无界面模式、代理 IP等配置,显著降低资源消耗并提升速度。实际案例中,该方案用于采集汽车点评数据,性能提升明显:内存占用降低 30%-50%,页面加载提速 40%-60%。结合技术架构图与演化树,全面解析爬虫技术演进,助力高效数据采集。
945 0
Headless Chrome 优化:减少内存占用与提速技巧
|
Web App开发 SQL 前端开发
性能工具之前端分析工Chrome Developer Tools性能标签
【2月更文挑战第22天】性能工具之前端分析工Chrome Developer Tools性能标签
478 1
性能工具之前端分析工Chrome Developer Tools性能标签
|
Web App开发 JavaScript
使用CRXjs、Vite、Vue 开发 Chrome 多页面插件,手动配置 vite.config.ts 和 manifest.json 文件
使用CRXjs、Vite、Vue 开发 Chrome 多页面插件,手动配置 vite.config.ts 和 manifest.json 文件
1170 0
|
Web App开发 存储 前端开发
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
840 0
浏览器原理 01 # Chrome架构:仅仅打开了1个页面,为什么有4个进程?
|
Web App开发 Windows
Windows【Chrome浏览器 01】首次安装的谷歌Chrome浏览器出现无法打开此页面问题处理(详细图文步骤)
Windows【Chrome浏览器 01】首次安装的谷歌Chrome浏览器出现无法打开此页面问题处理(详细图文步骤)
665 0
|
Web App开发
Chrome浏览器所有页面崩溃
Chrome浏览器所有页面崩溃
539 0
|
Web App开发 JSON 监控
Chrome浏览器扩展开发之自动化操作页面
Chrome浏览器扩展开发之自动化操作页面
1162 0
|
Web App开发 SQL JavaScript
【性能】7分钟带你了解【尤大】都在使用的 Chrome Runtime Performance Debug!
前段时间用Vue3开发了一个线上标准的项目,也都已经提测了,最近可以抽时间来整理一下项目中用到的技术栈,( 我用前端【最新】技术栈完成了一个生产标准的项目【Vue3 + TS + Vite + Pinia + Windicss + NavieUI】。 在开发之前特地重温了一下18年尤大的VUE CONF(杭州),过程中发现尤大使用到了这个Performance去分析JavaScript每一帧的计算,之前知道这个工具,但是平时基本不怎么用,借此机会学一下并做一些记录。
500 0
|
Web App开发 缓存 异构计算
猿大师播放器在Chrome网页播放多路H.265海康威视RTSP视频流页面卡顿CPU占用高怎么办?
试用猿大师播放器播放一路视频效果很不错,延迟可以控制在200毫秒左右,但是如果播放多路高清视频,CPU占用就会比较高,并且网页也会卡顿,该如何解决呢?
1013 0
猿大师播放器在Chrome网页播放多路H.265海康威视RTSP视频流页面卡顿CPU占用高怎么办?
chrome18-使用network waterfall分析页面载入性
chrome18-使用network waterfall分析页面载入性
544 0
chrome18-使用network waterfall分析页面载入性