最近研究了一下页面性能优化这一块,参照着 这篇文章 来实际动手做了一下
原文说的非常好,一路做下来确实很有成就感,页面的性能得到了非常大的提升,实时渲染帧率从原来十几帧提高到了接近 60 帧。
先嗮一下成果:
根据上图的结果来看,能够优化到这个程度确实算很不错了,但是在实际做的过程中,还是发现了许多问题
下面就说一下我在实际写的过程中的一些记录
一、新版 Chrome 里面 FPS 计数器的打开方法
文中说 FPS 计数器是自带的,于是我就去谷歌了下怎么打开,结果搜出来的都是老版 Chrome 的打开方法,当时的 FPS Meter 还在实验测试期呢,还要从 chrome://flags 里面去找。我找了找没找到,还以为没有这个功能了呢。
后来仔细一找,才发现这个功能已经可以在控制台里面找到了。下面说下新版 Chrome 里面打开 FPS 计数器的方法
F12 打开控制台 -> DevTools -> More Tools -> Rendering settings
然后在打开的面板上面勾上 FPS Meter 即可看到 FPS 计数器出来了
二、关于 requestAnimationFrame 这个函数
对于这个函数,个人感觉有点不稳定,时好时坏,之前在公司的电脑上试的时候,倒是出现了卡帧的现象,弄得我还以为这个函数不能用呢,过了几天再来试了试,居然又没问题了。。。总的来说感觉这个函数我在家里的电脑上试的时候,跟 setInterval 条件下的帧率进行比较,发现并没有太大区别,可能是现在的浏览器的渲染性能越来越强的缘故吧。不知道这个 API 在移动端上的表现怎样,感觉可能会出现兼容性问题。
在 PC 上跟 setInterval 的比较情况如下:
使用 setInterval:
使用 requestAnimationFrame:
要说二者的最大区别,应该就是实现方式了吧。通过上面的结果,不难发现,requestAnimationFrame 用的是递归式的调用,JS Heap 一直在上升,然后上升到一定程度之后就被 Chrome 的垃圾清理给释放掉了,如此反复。而 setInterval 的 JS Heap 曲线基本上保持稳定不变,因为它是是非递归式的,这样来看似乎 setInterval 比 requestAnimationFrame 更有优势一些,不过个人感觉其实差别真的不大,爱用哪个用哪个吧。
注:这个 API 已经被写入了 HTML5.1 标准
HTML 5.1-Web application APIs:Animation Frames
三、移除 jquery,改用原生 DOM 操作
随着 jquery 越来越庞大,越来越多的人开始诟病 jquery 的大小和性能问题,尤其是在这种以实时渲染为基础的应用中,使用 jquery 来进行 dom 操作也是影响帧率的重要因素,经过实际动手操作后,发现确实若不使用 jquery,dom 操作的时间会大幅缩短
下面这张是去掉 jquery 之前的情况:
对比一下下面这张去掉了 jquery 之后的情况:
可以看到,Scripting 脚本计算的时间大大缩短,取而代之的是 Rendering 渲染频率的提高,还是比较明显的能够发现去掉 jquery 能够带来的性能提升
四、使用 createDocumentFragment 拼接元素
使用这个方法的好处有不少,下面我就来随便列几点:
- 它能够比 jquery 更快地构造 DOM 树
- 能够实现在 for 循环构造完成 DOM 树以后再进行一次性的 append,减少反复 DOM 操作浪费的时间
- 去掉在 js 中的 html 代码,将视图构造转换为业务逻辑
- 当需要修改里面的小圆的位置时,可以直接从 js 中调出对应的 Element 进行修改,不需要再进行一次 DOM 查询操作
这是改良以后的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21var object = [];
var mainContainer = document.getElementsByClassName("main")[0];
var docFrag = document.createDocumentFragment();
for (var i = 0; i < COUNT; i++) {
var d = Math.random() * 2 * Math.PI;
var v = Math.random() * 5;
var circle = document.createElement("div");
circle.id = "circle-" + i;
circle.className = 'circle';
docFrag.appendChild(circle);
var vx = v * Math.cos(d);
var vy = v * Math.sin(d);
object.push({
x: 250,
y: 250,
vx: vx,
vy: vy,
dom: circle
});
}
mainContainer.appendChild(docFrag);
当需要修改小圆的位置时,进需要从 object 数组中取出对应的小圆进行修改即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22for (var i = 0; i < COUNT; i++) {
var x = object[i].x;
var y = object[i].y;
var vx = object[i].vx;
var vy = object[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;
x += vx;
y += vy;
object[i].x = x;
object[i].y = y;
object[i].vx = vx;
object[i].vy = vy;
object[i].dom.style.transform = 'translate(' + x + 'px,' + (400 - y) + 'px)';
}
最后附上优化好了之后的代码:
js 性能优化初体验——代码
参考资料:
使用 Chrome Timeline 来优化页面性能
使用 Chrome DevTools 的 Timeline 分析页面性能
网页性能管理详解:浅谈 chrome-Timeline 及 window.requestAnimationFrame()方法