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

优化前的准备:知己知彼

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

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

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

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

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

是不是很震撼,之前这个页面只是用来 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;

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

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

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

// 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;

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

优化三:替换 setInterval

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

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

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

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

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

如果我们用 transform 来代替 lefttop 来对元素进行定位,那么浏览器会为这个元素单独创立一个合成层,专门使用 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)';

看起来是不是很爽了?

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

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

未优化版 | 优化版

使用 Chrome Timeline 来优化页面性能 1相关推荐

  1. 使用 Chrome Timeline 来优化页面性能

    使用 Chrome Timeline 来优化页面性能 有时候,我们就是会不由自主地写出一些低效的代码,严重影响页面运行的效率.或者我们接手的项目中,前人写出来的代码千奇百怪,比如为了一个 Canvas ...

  2. 前端性能优化必备技能 - 利用 Chrome Dev Tools 进行页面性能分析

    背景 我们经常使用 Chrome Dev Tools 来开发调试,但是很少知道怎么利用它来分析页面性能,这篇文章,我将详细说明怎样利用 Chrome Dev Tools 进行页面性能分析及性能报告数据 ...

  3. 前端性能优化之利用 Chrome Dev Tools 进行页面性能分析

    背景 我们经常使用 Chrome Dev Tools 来开发调试,但是很少知道怎么利用它来分析页面性能,这篇文章,我将详细说明怎样利用 Chrome Dev Tools 进行页面性能分析及性能报告数据 ...

  4. web性能优化 页面性能优化小结

    总览 HTML 缩减资源的大小 缩减首屏内容的大小 启用压缩功能 按视口调整内容尺寸 压缩HTML代码 避免meta设置字符集 HTML内嵌小资源 CSS 优化CSS发送过程 合并外部CSS 简化CS ...

  5. 【重要!!】passive优化页面性能

    在js中给dom元素添加监听事件: let dom1=document.getElementById("box1"); function box(that){console.log ...

  6. Web前端教程分享:页面性能优化办法有哪些?

    引子 互联网有一项著名的8秒原则.用户在访问Web网页时,如果时间超过8秒就会感到不耐烦,如果加载需要太长时间,他们就会放弃访问.大部分用户希望网页能在2秒之内就完成加载.事实上,加载时间每多1秒,你 ...

  7. 页面性能优化办法有哪些? 1

    引子 互联网有一项著名的8秒原则.用户在访问Web网页时,如果时间超过8秒就会感到不耐烦,如果加载需要太长时间,他们就会放弃访问.大部分用户希望网页能在2秒之内就完成加载.事实上,加载时间每多1秒,你 ...

  8. 页面性能的基础因素 - 《Designing for Performance》

    页面性能的基础因素 最近读这本小书Designing for Performance,突然想到之前一篇网络性能评价只写了一半,在这里也里也算一个做个补充. 考虑页面性能可以从两种场景,第一个场景也是因 ...

  9. 使用Chrome DevTools的Timeline分析页面性能

    原文地址:http://horve.github.io/2015/10/26/timeli... 随着webpage可以承载的表现形式更加多样化,通过webpage来实现更多交互功能,构建web应用程 ...

  10. 前端页面性能优化指标

    前端页面性能优化指标 一.有哪些指标 LCP(Largest Contentful Paint) FID( First Input Delay) CLS(Cumulative Layout Shift ...

最新文章

  1. python关于字典的操作
  2. 【iCore3 双核心板】例程十七:USB_MSC实验——读/写U盘(大容量存储器)
  3. 3331付款方式怎么写_拼多多怎么刷单 为什么要刷单
  4. uva 11080(二分图染色)
  5. nginx修改默认端口
  6. A. Gamer Hemose
  7. 虚拟机Linux和宿主机传输文件
  8. 基于GitHub创建自己的个人网站
  9. python提取百度首页链接_python获取百度热榜链接的实例方法
  10. vue 钉钉授权第三方WEB网站扫码登录功能
  11. java错误代码1061_求助java大神,看下这是哪里出错了
  12. 一、传统能源仍具成长性
  13. Android开发之各种好看的背景颜色
  14. *item_search_similar - 搜索相似的商品**
  15. Web基础——JavaScript之事件绑定与事件对象
  16. 网络安全技术-百度百科大全
  17. 回溯法n后问题(具体代码实现,详细分析)
  18. pt-kill 用法记录
  19. 大学四年java学习的经验及心得体会
  20. 20121025 The django book 笔记 站点管理

热门文章

  1. 【OpenCV学习笔记】【编程实例】二(图像的旋转和翻转)
  2. Python在指定文件夹生成随机文件
  3. leetcode刷题日记-846. 一手顺子
  4. lableme标注的json文件转为mask r-cnn训练用的coco数据集格式
  5. 从零基础入门Tensorflow2.0 ----二、4.2 wide deep 模型(子类API)
  6. python查看我国1990-2015年间的温度变化情况
  7. scikit-learn 算法的通用形式
  8. python 绘制多个子图
  9. 设置MySQL最大连接数
  10. 鸿蒙比苹果流畅,华为鸿蒙应用恢复率优于苹果iOS,无惧老化36个月持续流畅