来源:javascript
http://blog.jobbole.com/31178/php
你的网站正常运转。如今咱们来让它运转的更快。网站的性能由页面载入速度和代码执行效率决定。一些服务可让你的网站载入更快,好比压缩JS和CDN,可是让代码执行的更快你要作的事情。css
代码中很小的改动均可能对性能形成巨大的影响。快速灵活的网站和可怕的“无响应脚本”对话框可能只有几行代码的差异。这篇文章告诉你如何经过用Chrome开发者工具(Chrome Developer Tools)找到这几行关键的代码。html
设置一个基线java
咱们来看一个简单的“颜色排序器”应用,这个应用展现了一个由各类颜色构成的网格,你能够拖拽这些颜色进行混合。每个点都是一个div标签加上一些让它看起来是圆的的CSS。git
生成这些颜色是须要技巧的,因此我借助了”Making Annoying Rainbows in Javascript”。github
页面载入的很快,但仍是花费了一些时间,在渲染以前还闪了一下。是时候对这个页面进行性能分析让它更快了。web
在开始作性能优化的时候要设置一个基线,来明确这个页面的速度到底怎样。这个基线可让你知道本身是否作了优化并帮助你权衡利弊。在这片文章里咱们要使用chrome开发者工具。chrome
性能分析器(profiler)是chrome开发者工具的一部分,点击小扳手下面的工具菜单就能够打开它。Firebug也有一些性能评测工具,可是webkit内核的浏览器(chrome和safari)在对代码进行性能分析和展现时间线方面是最棒的。Chrome还提供一种很棒的事件跟踪工具,叫作 speed tracer。浏览器
在时间线(timeline)标签下开始记录,载入页面而后中止记录,这样就设置了一个基线。(打开chrome开发者工具,点击“时间线”标签,而后点击窗口底部圆形的黑色“记录”图标开始记录)。chrome是很智能的,只有页面开始载入的时候才会开始记录。我记录了三次而后取了平均值,以防个人电脑在第一次测试的时候运行的很慢。
个人平均基线,也就是从第一个请求到页面所有渲染结束所花费的时间是1.25秒。这个时间不是太长,可是对于这样一个小的页面来讲也不算好。
我想让代码执行的更快,可是我并不知道是什么让它慢下来的。性能分析器(profiler)帮助我找到缘由。
建立一个Profile
时间线(timeline)告诉咱们代码运行花费的时间,可是并无帮助咱们知道代码运行的时候发生了什么。咱们能够作一些改动而后不断的测每次代码运行的时间,但这是盲目的。profiles给咱们提供了更好的方法。profiler告诉咱们哪些函数的执行占用了大部分时间。让咱们切换到chrome开发者工具的“Profiles”标签页开始性能测试,这里一共提供了三种类型的性能测试。
一、 javascript cpu 性能测试
显示javascript占用了多少CPU
二、 css选择器性能测试
显示处理CSS选择器占用的CPU
三、 堆栈快照
显示javascript对象的内存占用状况
咱们想要javascript代码执行的更快,因此咱们进行CPU性能测试。咱们开始性能测试,刷新页面而后中止。
经过性能分析首先知道不少函数在执行。“颜色排序器”使用了jQuery和jQuery UI,来处理些管理插件和解析表达式之类的事情。我发现列表最顶端的是decimalToHex和makeColorSorter两个函数。这两个函数占用了CPU13.2%的时间,这是作优化的好地方。
咱们能够点击函数调用旁边的“下一个”箭头来查看完整的函数调用堆栈。展开后,能够看到decimalToHex是被makeColorSorter调用的,makeColorSorter是经过$(document).ready调用的。
代码以下
1
2
3
4
|
$(document).ready(function() {
makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127, 121);
makeSortable();
});
|
弄清楚这两个函数是哪里调用的,也就弄清楚了让颜色能够排序并非最大的性能问题。一般状况下性能问题都是由多余的排序操做形成的,可是在个人代码中相比与排序增长DOM元素花费了更多时间。
我想要让这些函数执行的更快,可是首先我想要将个人改动区隔开。在页面载入过程当中会发生不少事情,我不想要这些影响到个人性能分析。
区隔问题
我作了第二个版本,这个版本中“颜色排序器”在我点击按钮以后才载入,而不是在document ready的时候载入。这就把文档载入的过程分离出去,让我能够只对颜色分类进行性能测试。调完性能以后我能够马上改回去。
让咱们调用新的函数testColorSorter并把它绑定到一个可点击的按钮上。
1
2
3
4
|
function testColorSorter() {
makeColorSorter(.05, .05, .05, 0, 2, 4, 128, 127, 121);
makeSortable();
}
|
1
|
<button id="clickMe" onclick="testColorSorter();">Click me</button>
|
1
|
|
在咱们进行性能分析以前改变应用可能致使意外的结果。这个改动看起来很安全,可是我仍是要从新运行性能检测器来看看我是否是无心中改变了什么。我会开始一次新的性能分析,点击应用中的按钮而后中止。
我首先注意到decimalToHex函数的载入只占用了4.23%的时间。这是代码执行花费时间最多的地方。咱们建立一个新的基线来看看这个方案对代码有多大的优化。
有些事件在我点击按钮以前有触发了,可是我只关注从我点击鼠标到浏览器渲染“颜色排序器”花费的时间。鼠标在390毫秒时点击,渲染事件在726毫秒处被触发。726减去390获得个人基线336毫秒。和第一个基线同样我重复了3次来取平均值。
这时,我知道如何得到而且获得了代码确切的运行时间,咱们已经准备好开始解决问题了。
让代码更高效
性能分析器只告诉咱们哪一个函数形成的问题,因此咱们要查看下函数的源码来了解函数作了些什么。
1
2
3
4
5
6
7
|
function decimalToHex(d) {
var hex = Number(d).toString(16);
hex = "00".substr(0, 2 - hex.length) + hex;
console.log('converting ' + d + ' to ' + hex);
return hex;
}
|
1
|
|
“颜色排序器”中的每个颜色点都有一个16进制的色彩值,例如#86F01B和#2345FE.这些值表示一种颜色中红,绿,蓝三原色各自的数值。例如的背景色是#2456FE,表明红色的值是36,绿色的值是86,蓝色的是254,每个数值必须是0到255之间的。
decimalToHex函数把这用RGB值表示的颜色转化为页面中咱们使用的16进制颜色。这个函数十分的简单,可是我仍是留下了一个能够去掉的调试代码console.log在那里。
decimalToHex 函数还在数字以前加上了补位。这是很重要的一点,由于有些10进制数字对应的是1个16进制数字。好比十进制中的10对应着16进制中的C,可是在CSS中须要一个两位数。为了让这个进制换算更快速,咱们让这段代码不是那么泛化。我知道每一个须要补位的数字长度都为1,因此咱们能够这样重写这个函数。
1
2
3
|
function decimalToHex(d) {
var hex = Number(d).toString(16);
return hex.length === 1 ? '0' + hex : hex; }
|
1
|
|
第三个版本的“颜色排序器”只有在须要补位的时候才改变字符串,而且不用调用substr函数。有了这个新函数,运行时间是137毫秒。再次对代码进行性能测试,能够发现decimalToHex函数只占用了总时间的%0.04,到了列表的下部。
咱们还能够发现占用CPU最多的函数是 jQuery的e.extend.merge。我不知道这个函数的做用,由于代码是压缩过的。我可使用开发版本的jQuery,可是我发现这个函数是被makeColorSorter调用的。因此下一步咱们先让这个函数执行的更快。
减少改动
“颜色排序器”中的多彩颜色是用过正弦曲线生成的。在光谱中设置一个中心点,而后以必定的偏移来建立这个曲线。这就把颜色变成了一个“彩虹模型”。咱们还能够经过改变红绿蓝三原色的使用频率来改变颜色。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
function makeColorSorter(frequency1, frequency2, frequency3,
phase1, phase2, phase3,
center, width, len) {
for (var i = 0; i < len; ++i)
{
var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center);
var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center);
var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center);
console.log('red: ' + decimalToHex(red));
console.log('green: ' + decimalToHex(green));
console.log('blue: ' + decimalToHex(blue));
var div = $('<div class="colorBlock"></div>');
div.css('background-color', '#' + decimalToHex(red) + decimalToHex(green) + decimalToHex(blue));
$('#colors').append(div);
}
}
|
1
|
|
咱们要去掉console.log函数。这些调用很是的糟糕,由于每次执行都会调用decimalToHex函数,这意味着decimalToHex函数会被多调用2倍的次数。这个函数大幅度的改变了DOM结构。每次循环,都向id为colors的div中添加一个新的div。这就让我怀疑这就是e.extend.mergefunction作的事情。用性能分析器作一个小实验就能够搞清楚。
我想要一次把全部的div添加进去,而不是在每一个循环中添加一个新的div。建立一个变量来存储数据,而后在最后一次性添加进去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function makeColorSorter(frequency1, frequency2, frequency3,
phase1, phase2, phase3,
center, width, len) {
var colors = "";
for (var i = 0; i < len; ++i)
{
var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center);
var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center);
var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center);
colors += '<div class="colorBlock" style=" margin: 0px; padding: 0px; font-size: inherit !important; font-family: inherit; height: inherit; line-height: inherit !important; font-weight: inherit !important; color: rgb(0, 111, 224) !important;"> +
decimalToHex(red) + decimalToHex(green) + decimalToHex(blue) + '"></div>';
}
$('#colors').append(colors);
}
|
1
|
|
这个小改动意味着DOM只在添加全部div的时候作一次改变。用时间线进行测试,咱们发现从点击到渲染花费了31毫秒。这个dom变更,使得第四个版本的运行时间下降了86%。我能够再次打开性能分析器(profiler),发现e.extend.merge函数占用了不多的时间,在列表中已经看不到它了。
咱们还能够彻底移除decimalToHex函数让代码更快一点。由于CSS支持RGB颜色,因此咱们不须要把他们转换到16进制。如今咱们能够这样写makeColorSorter函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function makeColorSorter(frequency1, frequency2, frequency3,
phase1, phase2, phase3,
center, width, len) {
var colors = "";
for (var i = 0; i < len; ++i)
{
var red = Math.floor(Math.sin(frequency1 * i + phase1) * width + center);
var green = Math.floor(Math.sin(frequency2 * i + phase2) * width + center);
var blue = Math.floor(Math.sin(frequency3 * i + phase3) * width + center);
colors += '<div class="colorBlock" style=" margin: 0px; padding: 0px; font-size: inherit !important; font-family: inherit; height: inherit; line-height: inherit !important; font-weight: inherit !important; color: rgb(0, 111, 224) !important;"> +
red + ',' + green + ',' + blue + ')"></div>';
}
$('#colors').append(colors);
}
|
1
|
|
第五个版本的执行只用了26毫秒并且代码行数从28行减小到18行。
在你的应用中进行Javascript性能分析
实际工做中的应用要比“颜色排序器”复杂的多,可是作性能分析要遵循一样的基本原则
一、 设置一个基线,这样你就知道你是从何处开始的。
二、 把问题从应用的其余代码隔离出来。
三、 在一个可控的环境下进行优化,频繁的使用时间线(timelines)和性能分析器(profiles)
还有一些性能优化的准则
一、 从最慢的部分开始,这样在时间优化上能够获得最大的提高。
二、 控制环境。若是你换了电脑或者作了任何大的改动,都要设置新的基线。
三、 屡次分析以防你电脑的异常致使获得不正确的结果。
每一个人都想要他的网站更快,你必须开发新的功能,可是新的功能一般会让网站更慢。因此花费时间来作性能优化是有价值的。
性能分析和优化使得最终版颜色分类器的执行时间减小了92%。你的网站能够变快多少?