使用 Chrome Timeline 来优化页面性能

使用 Chrome Timeline 来优化页面性能

使用 Chrome Timeline 来优化页面性能

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

优化前的准备:知己知彼

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

2

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

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

3

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

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

<!-- 一段小动画:点击按钮之后会有一个爆炸的粒子效果 --> <!DOCTYPE html> <html> <head>  <meta charset="utf-8">  <title>Test</title>  <style>  .main {  position: relative;  width: 500px;  height: 500px;  background: #000;  overflow: hidden;  }  .circle {  position: absolute;  border-radius: 50%;  border: 1px solid #FFF;  width: 8px;  height: 8px;  }  </style> </head> <body>  <div class="main"></div>  <hr>  <button onclick="showAnimation()">点我</button>  <script src="jquery.min.js"></script>  <script src="animation.js"></script> </body> </html> 
// animation.js

// 粒子总数
var COUNT = 500;
// 重力
var G = -0.1;
// 摩擦力
var F = -0.04;

function init() {
 for (var i = 0; i < COUNT; i++) {  var d = Math.random() * 2 * Math.PI;  var v = Math.random() * 5;  var circle = $('<div id="circle-' + i + '" class="circle" data-x="250" data-y="250" data-d="' + d + '" data-v="' + v + '"></div>');  circle.appendTo($('.main'));  } } function updateCircle() {  for (var i = 0; i < COUNT; i++) {  var x = parseFloat($('#circle-' + i).attr('data-x'));  var y = parseFloat($('#circle-' + i).attr('data-y'));  var d = parseFloat($('#circle-' + i).attr('data-d'));  var v = parseFloat($('#circle-' + i).attr('data-v'));  var vx = v * Math.cos(d);  var vy = v * Math.sin(d);  if (Math.abs(vx) < 1e-9) vx = 0;  // 速度分量改变  vx += F * Math.cos(d);  vy += F * Math.sin(d) + G;  // 计算新速度  v = Math.sqrt(vx * vx + vy * vy);  if (vy > 0) d = Math.acos(vx / v);  else d = -Math.acos(vx / v);  // 位移分量改变  x += vx;  y += vy;  $('#circle-' + i).attr('data-x', x);  $('#circle-' + i).attr('data-y', y);  $('#circle-' + i).attr('data-d', d);  $('#circle-' + i).attr('data-v', v);  $('#circle-' + i).css({'top': 400 - y, 'left': x});  } } var interval = null; function showAnimation() {  if (interval) clearInterval(interval);  $('.main').html('');  init();  interval = setInterval(updateCircle, 1000 / 60); } 

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

1

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

4

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

5

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

6

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

优化一:减少 DOM 操作

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

var objects = [];
// 在 init 函数中
objects.push({
 x: 250,  y: 250,  d: d,  v: v }); // 在 updateCircle 函数中 var x = objects[i].x; var y = objects[i].y; var d = objects[i].d; var v = objects[i].v; // …. objects[i].x = x; objects[i].y = y; objects[i].d = d; objects[i].v = v; 

7

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

8

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

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

// init
var vx = v * Math.cos(d);
var vy = v * Math.sin(d);
objects.push({
 x: 250,  y: 250,  vx: vx,  vy: vy }); // updateCircle var vx = objects[i].vx; var vy = objects[i].vy; // 计算新速度 var v = Math.sqrt(vx * vx + vy * vy); if (Math.abs(vx) < 1e-9) vx = 0; // 速度分量改变 vx += F * vx / v; vy += F * vy / v + G; // …. objects[i].vx = vx; objects[i].vy = vy; 

9

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

优化三:替换 setInterval

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

10

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

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

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

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

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

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

11

看起来是不是很爽了?

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

P.S. 全部的代码在这里,欢迎吐槽:

未优化版 | 优化版

转载于:https://www.cnblogs.com/Coding-net/p/5756316.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108912.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • django笔记_django 异步

    django笔记_django 异步前言Django是一个开放源代码的Web应用框架,由Python写成,最初用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站,即CMS(内容管理系统)软件,于2005年7月在BSD许可证下发布,这

    2022年8月7日
    4
  • vue 路由嵌套_vue路由实现方式

    vue 路由嵌套_vue路由实现方式嵌套路由有时候在路由中,主要的部分是相同的,但是下面可能是不同的。比如访问首页,里面有新闻类的/home/news,还有信息类的/home/message。这时候就需要使用到嵌套路由。项目结构如下:

    2022年7月31日
    5
  • Linux/Centos安装oracle11G数据库-史上最详细的图文安装数据库方法「建议收藏」

    Linux/Centos安装oracle11G数据库-史上最详细的图文安装数据库方法「建议收藏」1.环境介绍本文是在CentOS7.0x64系统上安装oracle11g如下图所示:物理内存应不少于1GB,对于VMWARE虚拟机建议不少于1200MB.对于系统中的交换分区设置,应参以下约定:物理内存为1GB~2GB时,交换分区为物理内存的1.5~2倍;物理内存为2GB~16GB时,交换分区与物理内存大小相同;物理内存超过16GB时,交换分区使用16GB就可以了。如下图所示:2.安装准备对于6…

    2022年5月30日
    32
  • visio2016专业版2018最新密钥和下载方法 整理

    visio2016专业版2018最新密钥和下载方法 整理visio2016是目前微软最强大的图标制作软件,也是很难激活的。今天小编就给大家分享一下visio2016专业版的下载地址和激活方法,希望能给你带来方便。一,所有版本下载和更多有效的密钥www.dnbbn.com当然,也可以通过官网下载,登录setup.office.com.输入密钥识别后即可下载。二,激活。下载后可以输入一下密钥,但是说明一下,只适用于上述的专业版哈,其他版本无…

    2022年8月13日
    4
  • python模拟键盘输入_python控制鼠标键盘

    python模拟键盘输入_python控制鼠标键盘win32api.keybd_event该函数原型:keybd_event(bVk,bScan,dwFlags,dwExtraInfo)第一个参数:虚拟键码(键盘键码对照表见附录);第二个参数:硬件扫描码,一般设置为0即可;第三个参数:函数操作的一个标志位,如果值为KEYEVENTF_EXTENDEDKEY则该键被按下,也可设置为0即可,如果值为KEYEVENTF_KEYUP则该按键被释放;…

    2022年10月11日
    2
  • python中导入numpy为什么错误_pycharm安装配置教程

    python中导入numpy为什么错误_pycharm安装配置教程今天网上复制了一个代码,其中有个importnumpyasnp,运行时提示需要安装numpy库,然后我按照网上的方法,按顺序点击File–>Settings–>Project:pythonProject–>PythonInterpreter,然后找到+那里准备添加库,如下:然后就报erroroccurredwheninstallingpackage”numpy”的错误,搞了半天都没搞定,遂找了一个经……

    2022年8月29日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号