运用 Chrome Timeline 来优化页面机能

有时刻,我们就是会情不自禁地写出一些低效的代码,严重影响页面运转的效力。或许我们接办的项目中,前人写出来的代码光怪陆离,比方为了一个 Canvas 殊效须要同时绘制 600 个三角形,又比方 Coding.net 的使命中间须要同时 watch 上万个变量的变化等等。那末,假如我们遇到了一个比较低效的页面,应当怎样去优化它呢?

优化前的预备:知己知彼

在统统最先之前,我们先翻开 F12 面板,熟习一下我们接下来要用到的东西:Timeline:

《运用 Chrome Timeline 来优化页面机能》

嗯没错就是它。下面一一引见一下吧。地区 1 是一个缩略图,能够看到除了时刻轴之外被高低分成了四块,离别代表 FPS、CPU 时刻、网络通信时刻、客栈占用;这个缩略图能够横向缩放,白色地区是下面能够看到的时刻段(灰色固然是不可见的啦)。地区 2 能够看一些交互事宜,比方你转动了一下页面,那末这里会涌现一个 scroll 的线段,线段掩盖的局限就是转动经由的时刻。地区 3 则是详细的事宜列表了。

一最先没有纪录的时刻,一切的地区都是空的。最先统计和完毕统计都很简朴,左上角那坨黑色的圆圈就是。它右侧谁人长得像“禁止通行”的按钮是用来消灭现有纪录的。当有数据的时刻,我们把鼠标滚轮向上滚,能够看到地区被放大了:

《运用 Chrome Timeline 来优化页面机能》

短短的时刻里,阅读器做了这么多事变。关于平常的屏幕,原则上来讲一秒要往屏幕上绘制 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 调试东西自带的):

《运用 Chrome Timeline 来优化页面机能》

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

《运用 Chrome Timeline 来优化页面机能》

好吧,翻开 Timeline,按下纪录按钮,点一下页面中的“点我”,轻微过一会儿住手纪录,就会获得一些数据。放大一些,对 jQuery 比较熟习的同砚能够看出来,这些大部分是 jQuery 的函数。我们点一下谁人 updateCircle 的区块,然后看下面:

《运用 Chrome Timeline 来优化页面机能》

这里通知我们,这个函数运转了多久、函数代码在哪儿。我们点一下谁人链接,因而就跳到了 Source 页:

《运用 Chrome Timeline 来优化页面机能》

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

《运用 Chrome Timeline 来优化页面机能》

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

《运用 Chrome Timeline 来优化页面机能》

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

所以最耗时的那句话已变成了盘算 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;

《运用 Chrome Timeline 来优化页面机能》

只要加减乘除和开平方运算,每次比本来的时刻又少了两毫秒。从流通的角度来讲实在已能够满帧运转了,然则为何我照样以为偶然会有点卡呢?

优化三:替代 setInterval

既然偶然会掉帧,那末就看看是怎样掉的呗~原则上来讲,在每一次阅读器举行绘制之前,Timeline 内里应当有一个叫 Paint 的事宜,就像如许:

《运用 Chrome Timeline 来优化页面机能》

看到这些绿色的东西了没?就是它们!看上面的时刻轴,虽然代码中 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)';

《运用 Chrome Timeline 来优化页面机能》

看起来是否是很爽了?

实在,优化是无止境的,比方我在 init 函数中完整能够不必 jQuery,改用 createDocumentFragment 来拼接元素,如许初始化的时刻就能够急剧收缩;换取 updateCircle 中的几个语句的递次,在 V8 引擎下效力能够会有肯定的提拔;以至还能够连系 Profile 面板来剖析内存占用,检察阅读器画图的细节……然则个人感觉并用不到这么极限的优化。关于一个项目来讲,假如纯真为了优化而写一些新鲜的代码,是很不合算的。

P.S. 悉数的代码在这里,迎接吐槽:

未优化版 | 优化版

    原文作者:CODING
    原文地址: https://segmentfault.com/a/1190000006224653
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞