來源: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%。你的網站能夠變快多少?