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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403317.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

el-table点击某行高亮;el-table设置hover效果;监听el-table点击某行变色;去除el-table的hover效果;监听el-table的hover事件

以下代码可直接复制使用 无需任何修改 注意&#xff1a;如果最后的的css不生效&#xff0c;就单独的放在一个style标签内 重点解释&#xff1a; :row-class-name"tableRowClassName" //这个是设置返回某行的类名 :stripe"false" …

SQL Server replication requires the actual server name to make a connection to the server.错误解决...

今天&#xff0c;在作数据发库时&#xff0c;出现如下错误&#xff1a; "SQL Server replication requires the actual server name to make a connection to the server. Connections through a server alias, IP address, or any other alternate name are not supporte…

el-table使用fixed后,最后一行显示不全;el-table设置fixed后,最后一行被遮挡。

解决代码&#xff1a; .el-table {/deep/ .el-table__body-wrapper {overflow-x: scroll !important;//直接设置横轴内容滚动条 即可撑高高度 还不影响使用} }一、情景&#xff1a;给element的el-table设置fixed固定后&#xff0c;最后一行显示部分被遮挡。有的说是有时出现这…

修改el-table滚动条样式

.el-table {// 滚动条的宽度/deep/ .el-table__body-wrapper::-webkit-scrollbar {width: 10px; // 横向滚动条height: 10px; // 纵向滚动条 必写}// 滚动条的滑块/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb {background-color: #1fff;//颜色border-radius: 5px…

Mac OS 如何更改文件的默认打开方式

From: http://www.ipc.me/os-x-xiao-ji-qiao.html 和使用 Windows PC 一样&#xff0c;人们在 Mac 电脑中往往也需要用多种应用开启同一种文件。并且&#xff0c;由于环境、习惯以及个人喜好不同&#xff0c;用户常常会希望更改某种文件的默认打开方式…… 在 OS X 系统下&a…

el-table列宽自适应;el-table表格的列根据内容自动撑满;el-table内容换行问题;

原文链接 重点就一个方法 :width"flexColumnWidth(‘date’,tableData)" 第一个参数是这个列的字段 第二个参数是这个表格的数据 只需要给对应的列 el-table-column 标签加上此方法 即可做到此列自适应 其他的列想要自适应宽度 同样使用 :width 方法即可 此段代码可…

Android初级教程:Android中解析方式之pull解析

在安卓中有很多种解析方式。按照大方向有xml解析和json解析。而&#xff0c;细致的分&#xff0c;xml和json解析各有自己的很多解析方式。今天这一篇主要介绍xml解析中的pull解析。对于xml的解析方式&#xff0c;我之前在javaweb一些知识中有写过dom和dom4j等等解析方式。有兴趣…

vi 多窗口同步滚动--适用于人工文件比较

vi本身带有文件比较功能&#xff0c;这个就不多说了&#xff0c;有时我们需要同时查看两个非常类似的文件&#xff0c;但是却有少许区别的地方&#xff0c;当然用专业的文件比较工具自然是非常方便的。 这里我只想提一下&#xff0c;vi实现多窗口同步滚动的方法, 先用vi打开两个…

sqlserver 把SELECT结果集中一列的所有的值 用逗号隔开放进一个字段内

首先我们有一个表 查询结果如下: 现在我们想要把USER_NAME这一列的内容 放到一个字段里面去: 一行sql语句解决问题: SELECT STUFF(( SELECT , convert(VARCHAR, USER_NAME) FROM TE_USER FOR XML PATH()), 1, 1, ) AS UserName解释一下上面这条语句: 1,首先STUFF函数在上面的…

java-多线程-一道阿里面试题分析

2019独角兽企业重金招聘Python工程师标准>>> 传说这是阿里的一道面试题&#xff1a; 也传说发这道题出来的作者去了tmail。下面是关于题目的描述: 这段代码大多数情况下运行正常&#xff0c;但是某些情况下会出问题。什么时候会出现什么问题&#xff1f;如何修正&a…

简单话题:LED呼吸灯和串口LED指示灯

最近在串口发送引脚上接LED指示发送或者接收状态&#xff0c;但是需求是希望连续发送或者接收字符过程中LED闪烁&#xff0c;而不是保持在一个常量或者常灭的状态。首先&#xff0c;把实际电路图和串口时序贴出来: 可以看出串口发送接收空闲态为高电平&#xff0c;只要进行数据…

mac下使用sshpass实现ssh记住密码

From: http://tinyhema.iteye.com/blog/2093795 由于有一些场景不能使用ssh私钥来实现免登&#xff0c;因此需要想其它办法解决一下这个问题。 安装sshpass 试图使用homebrew安装 Shell代码 $ brew install sshpass Error: No available formula for sshpass We wont …

ESXI忘记密码怎么办?

忘记ESX root用户的密码怎么办? 以单用户模式进入COS&#xff0c;然后修改root密码&#xff0c;既可搞定。第1步&#xff1a;打开/重启ESX主机图1第2步&#xff1a;到GRUB菜单处 (图2) &#xff0c;用键盘上下键&#xff0c;将光标放在“VMware ESX 4.0”上&#xff0c;按“a”…

TCPDUMP/LIBPCAP 1-由零开始

简介 TCPDUMP是强大的网络包分析器&#xff0c;可以在线或离线抓包&#xff0c;设置过滤条件等操作。 LIBPCAP是十分简洁易用的C/C网络流量抓包库&#xff1b;实际上TCPDUMP就是基于LIBPCAP实现的一个应用程序。为什么要学会工具和库的使用 工欲善其事必先利其器&#xff0c…

TCPDUMP/LIBPCAP 2-搭建环境

1. 实验平台   Linux 发行版本众多&#xff0c;考虑到大部分开发者的习惯&#xff0c;因此决定采用桌面版系统&#xff0c;具体为 CentOS-6.5-x86_64。系统内核版本为&#xff1a;2.6.32。尽管内核版本较低&#xff0c;但考虑到兼容性和稳定性&#xff0c;选择了此版本系统作…

使用jquery的blockui插件显示弹出层

使用jquery的blockui插件显示弹出层 Posted on 2011-04-14 16:34 孤独者 阅读(9975) 评论(0) 编辑 收藏 在做网站的开发过程中&#xff0c;可能需要使用弹出层&#xff0c;使用jquery的blockui插件可以很轻松的实现这个效果。blockui可以在你发送ajax请求的时候&#xff0c;显…

提升用户体验,你不得不知道的事儿——三种提醒框的微技巧

大家都知道无论是android开发还是其他的开发&#xff0c;用户的体验都是很重要的事儿&#xff0c;下面就android开发中的三种提醒方式&#xff0c;Toast,SnackBar,Dialog做一些细节上的处理&#xff0c;或许能让你的产品更有用户亲和力。 1&#xff09;Toast Toast是一个轻量级…

el-table列宽设置百分比无效;el-table使用min-width设置百分比;el-table百分比设置无效;

废话不多说&#xff0c;直接给每个el-table-column&#xff0c;设置 width"auto" min-width"25%"即可。 总的百分比还是要等于100%哈。 点赞收藏吧 感谢 代码可以直接复制使用&#xff1a; <template><div style"width:1300px;">&…

VIM使用系统剪切板

在 Linux 终端模式下使用 vim 编辑器时发现经常需要在vim打开的文本文档进行复制粘贴&#xff0c;那么下面就跟着我的思路一步步往下走吧。 一、首先确认当前 vim 配置是不是支持系统剪切板&#xff0c;可以在终端模式下输入命令&#xff1a; vim --version | grep clipboard…

python操作Excel读写--使用xlrd

From: http://www.cnblogs.com/lhj588/archive/2012/01/06/2314181.html 一、安装xlrd模块 到python官网下载http://pypi.python.org/pypi/xlrd模块安装&#xff0c;前提是已经安装了python 环境。 也可以在命令行执行&#xff1a;easy_install xlrd (注意权限) 二、使用介绍…