謹記:80%-90%的終端響應時間是花費在下載頁面中的圖片,樣式表,腳本,flash等;javascript
詳細的解釋來這裏查:http://developer.yahoo.com/performance/rules.htmlcss
簡單翻譯解釋下:html
一、儘可能減小HTTP請求個數——須權衡前端
合併圖片(如css sprites,內置圖片使用數據)、合併CSS、JS,這一點很重要,可是要考慮合併後的文件體積。java
二、使用CDN(內容分發網絡)node
這裏能夠關注CDN的三類實現:鏡像、高速緩存、專線,以及智能路由器和負載均衡;web
三、爲文件頭指定Expires或Cache-Control,使內容具備緩存性。數據庫
區分靜態內容和動態內容,避免之後頁面訪問中沒必要要的HTTP請求。json
四、避免空的src和href跨域
留意具備這兩個屬性的標籤如link,script,img,iframe等;
五、使用gzip壓縮內容
Gzip壓縮全部可能的文件類型以來減小文件體積
六、把CSS放到頂部
實現頁面有秩序地加載,這對於擁有較多內容的頁面和網速較慢的用戶來講更爲重要,同時,HTML規範清楚指出樣式表要放包含在頁面的區域內;
七、把JS放到底部
HTTP/1.1 規範建議,瀏覽器每一個主機名的並行下載內容不超過兩個,而問題在於腳本阻止了頁面的平行下載,即使是主機名不相同
八、避免使用CSS表達式
頁面顯示和縮放,滾動、乃至移動鼠標時,CSS表達式的計算頻率是咱們要關注的。能夠考慮一次性的表達式或者使用事件句柄來代替CSS表達式。
九、將CSS和JS放到外部文件中
咱們須要權衡內置代碼帶來的HTTP請求減小與經過使用外部文件進行緩存帶來的好處的折中點。
十、減小DNS查找次數
咱們須要權衡減小 DNS查找次數和保持較高程度並行下載二者之間的關係。
十一、精簡CSS和JS
目的就是減小下載的文件體積,可考慮壓縮工具JSMin和YUI Compressor。
十二、避免跳轉
爲了確保「後退」按鈕能夠正確地使用,使用標準的 3XXHTTP狀態代碼;同域中注意避免反斜槓 「/」 的跳轉;
跨域使用 Alias或者 mod_rewirte創建 CNAME(保存一個域名和另一個域名之間關係的DNS記錄)
1三、剔除重複的JS和CSS
重複調用腳本,除了增長額外的HTTP請求外,屢次運算也會浪費時間。在IE和Firefox中無論腳本是否可緩存,它們都存在重複運算JavaScript的問題。
1四、配置ETags
Entity tags(ETags)(實體標籤)是web服務器和瀏覽器用於判斷瀏覽器緩存中的內容和服務器中的原始內容是否匹配的一種機制(「實體」就是所說的「內 容」,包括圖片、腳本、樣式表等),是比last-modified date更更加靈活的機制,單位時間內文件被修過屢次,Etag能夠綜合Inode(文件的索引節點(inode)數),MTime(修改時間)和 Size來精準的進行判斷,避開UNIX記錄MTime只能精確到秒的問題。 服務器集羣使用,可取後兩個參數。使用ETags減小Web應用帶寬和負載。
1五、使AJAX可緩存
利用時間戳,更精巧的實現響應可緩存與服務器數據同步更新。
1六、儘早刷新輸出緩衝
尤爲對於css,js文件的並行下載更有意義
1七、使用GET來完成AJAX請求
當使用XMLHttpRequest時,瀏覽器中的POST方法是一個「兩步走」的過程:首先發送文件頭,而後才發送數據。在url小於2K時使用GET獲取數據時更加有意義。
1八、延遲加載
肯定頁面運行正常後,再加載腳原本實現如拖放和動畫,或者是隱藏部分的內容以及摺疊內容等。
1九、預加載
關注下無條件加載,有條件加載和有預期的加載。
20、減小DOM元素個數
使用更適合或者在語意是更貼切的標籤,要考慮大量DOM元素中循環的性能開銷。
2一、根據域名劃分頁面內容
很顯然, 是最大限度地實現平行下載
2二、儘可能減小iframe的個數
考慮即便內容爲空,加載也須要時間,會阻止頁面加載,沒有語意,注意iframe相對於其餘DOM元素高出1-2個數量級的開銷,它會在典型方式下阻塞onload事件,IE和Firefox中主頁面樣式表會阻塞它的下載。
2三、避免404
HTTP請求時間消耗是很大的,有些站點把404錯誤響應頁面改成「你是否是要找***」,這雖然改進了用戶體驗可是一樣也會浪費服務器資源(如數 據庫等)。最糟糕的狀況是指向外部 JavaScript的連接出現問題並返回404代碼。首先,這種加載會破壞並行加載;其次瀏覽器會把試圖在返回的404響應內容中找到可能有用的部分當 做JavaScript代碼來執行。
2四、減小Cookie的大小
去除沒必要要的coockie
使coockie體積儘可能小以減小對用戶響應的影響
注意在適應級別的域名上設置coockie以便使子域名不受影響
設置合理的過時時間。較早地Expire時間和不要過早去清除coockie,都會改善用戶的響應時間。
2五、使用無cookie的域
肯定對於靜態內容的請求是無coockie的請求。建立一個子域名並用他來存放所
有靜態內容。
2六、減小DOM訪問
緩存已經訪問過的有關元素
線下更新完節點以後再將它們添加到文檔樹中
避免使用JavaScript來修改頁面佈局
2七、開發智能事件處理程序
有時候咱們會感受到頁面反應遲鈍,這是由於DOM樹元素中附加了過多的事件句柄而且些事件句病被頻繁地觸發。這就是爲何說使用event delegation(事件代理)是一種好方法了。若是你在一個div中有10個按鈕,你只須要在div上附加一次事件句柄就能夠了,而不用去爲每個按 鈕增長一個句柄。事件冒泡時你能夠捕捉到事件並判斷出是哪一個事件發出的。
你一樣也不用爲了操做DOM樹而等待onload事件的發生。你須要作的就是等待樹結構中你要訪問的元素出現。你也不用等待全部圖像都加載完畢。
你可能會但願用DOMContentLoaded事件來代替 事件應用程序中的onAvailable方法。
2八、用 代替@import
在IE中,頁面底部@import和使用做用是同樣的,所以最好不要使用它。
2九、避免使用濾鏡
徹底避免使用AlphaImageLoader的最好方法就是使用PNG8格式來代替,這種格式能在IE中很好地工做。若是你確實須要使用 AlphaImageLoader,請使用下劃線_filter又使之對IE7以上版本的用戶無效。
30、優化圖像
嘗試把GIF格式轉換成PNG格式,看看是否節省空間。在全部的PNG圖片上運行pngcrush(或者其它PNG優化工具)
3一、優化CSS Spirite
在Spirite中水平排列你的圖片,垂直排列會稍稍增長文件大小;
Spirite中把顏色較近的組合在一塊兒能夠下降顏色數,理想情況是低於256色以便適用PNG8格式;
便於移動,不要在Spirite的圖像中間留有較大空隙。這雖然不大會增長文件大小但對於用戶代理來講它須要更少的內存來把圖片解壓爲像素地圖。 100×100的圖片爲1萬像素,而1000×1000就是100萬像素。
3二、不要在HTML中縮放圖像——須權衡
不要爲了在HTML中設置長寬而使用比實際須要大的圖片。若是你須要:
<img width=」100″ height=」100″ src=」mycat.jpg」 alt=」My Cat」 />
那麼你的圖片(mycat.jpg)就應該是100×100像素而不是把一個500×500像素的圖片縮小使用。這裏在下文有更有趣的分析。
3三、favicon.ico要小並且可緩存
favicon.ico是位於服務器根目錄下的一個圖片文件。它是一定存在的,由於即便你不關心它是否有用,瀏覽器也會對它發出請求,所以最好不要 返回一 個404 Not Found的響應。因爲是在同一臺服務器上,它每被請求一次coockie就會被髮送一次。這個圖片文件還會影響下載順序,例如在IE中當你在 onload中請求額外的文件時,favicon會在這些額外內容被加載前下載。
所以,爲了減小favicon.ico帶來的弊端,要作到:
文件儘可能地小,最好小於1K
在適當的時候(也就是你不要打算再換favicon.ico的時候,由於更換新文件時不能對它進行重命名)爲它設置Expires文件頭。你能夠很安全地 把Expires文件頭設置爲將來的幾個月。你能夠經過覈對當前favicon.ico的上次編輯時間來做出判斷。
Imagemagick能夠幫你建立小巧的favicon。
3四、保持單個內容小於25K
由於iPhone不能緩存大於25K的文件。注意這裏指的是解壓縮後的大小。因爲單純gizp壓縮可能達不要求,所以精簡文件就顯得十分重要。
3五、打包組件成複合文本
頁面內容打包成複合文本就如同帶有多附件的Email,它可以使你在一個HTTP請求中取得多個組件(切記:HTTP請求是很奢侈的)。當你使用這條規 則時,首先要肯定用戶代理是否支持(iPhone就不支持)。
2、Yahoo軍規以外的場景?
一、 使用json做爲數據的交換格式
Json在瀏覽器解析的效率至少高於XML一個數量級,高級瀏覽器中內置的有生成和解析json的方法,IE6中要用額外的方法(http://json.org ),不要用eval,容易引起性能和安全問題。
二、 儘量對images和table設定寬高值
針對Yslow的不要在HTML中縮放圖像——第33條,有人會誤解爲不要對圖片加寬高值,其實這條建議自己的意思是不要爲了獲取一個特定大小的圖片,而去強行經過設置寬高值拉伸或者壓縮一個既有的圖片。建議是另存一張符合尺寸的圖片替代。
對圖片和table是設定寬高,是考慮到若是瀏覽器能馬上知道圖片或者tables的寬高,它就可以直接呈現頁面而不須要經過計算元素大小後重繪,並且即使是圖片損毀而沒有展示,也不會進而破壞了頁面原本的佈局。
有一些應用場景須要注意:
a、批量圖片,圖片源可控同時頁面圖片寬高值不可變,好比數據庫有100張100*100的圖片要在頁面中所有展現,那麼建議是都寫上
XHTML
<img width=」100″ height=」120″ src=」" alt=」" />
b、批量圖片,圖片源不可控同時頁面圖片寬高值不可變,好比數據庫有100張圖片,而已知圖片有的尺寸是97*100,有的100*105,而又 不可能去一張張修改另存。這裏視狀況而定,根據圖片尺寸與要求尺寸的偏離度,在保證圖片不拉伸變形同時不影響頁面佈局的狀況下,能夠對圖片單獨設定寬度 100,同時對其包裹的容器設定100*100的寬高來隱藏多出來的部分,注意不能同時設置寬高以防止變形。
c、批量圖片,圖片源不可控,頁面圖片寬高值不定,好比數據庫有100張各類尺寸誤差較大的,此時可不對圖片設置寬高;
其餘狀況不一一羅列,原則是在最大程度保證圖片不變形與圖片最大面積展示的前提下,儘量爲圖片設置寬高值,總之就是權衡。
Tables的寬高值同圖片,儘量設置。
三、 拆離內容塊
儘可能用div取代tables,或者將tables打破成嵌套層次深的結構;
避免用這樣的嵌套
<table>
<table>
<table>
...
</table>
</table>
</table>
採用下面的或者div重構:
<table></table>
<table></table>
<table></table>
四、 高效的CSS書寫規則
衆所周知,CSS選擇符是從右向左進行匹配的。
一般一個圖片列表的的小模塊
<div id="box">
<div class="hd">
<h3>個人旅途</h3>
</div>
<div class="bd">
<h4>旅途1</h4>
<ul id="pics">
<li>
<a href="#pic" title=""><img src="" alt="" /> </a>
<p>這是在<strong>圖片1</strong></p>
</li>
</ul>
</div>
</div>
爲了代碼上縮進後內層的整潔性,咱們html有可能這樣寫以外,更喜歡看這樣的css寫法:
.box{border:1px solid #ccc }
.box .hd{border-bottom:1px solid #ccc }
.box .hd h3{color:#515151}
.box .bd{color:#404040 }
.box .bd ul{margin-left:10px}
.box .bd ul li{border-bottom:1px dashed #f1f1f1}
.box .bd ul li a{text-decoration:none}
.box .bd ul li a:hover{text-decoration:underline}
.box .bd ul li a img{border:1px solid #ccc}
.box .bd ul li p{text-align:left;}
.box .bd ul li p strong{color:#ff6600}
其實寫到這裏,問題已經顯而易見了。深達五層抑或六層的嵌套,同時右邊的選擇符都是採用標籤,在知足咱們視覺平整與代碼結構系統化的時候,付出的是性能的代價。
不作進一步的代碼書寫方式的探討,受我的習慣與應用場景影響。這裏對css選擇符按照開銷從小到大的順序梳理一下:
ID選擇符 #box
類選擇符 .box
類型選擇符 div
相鄰兄弟選擇符 h4 + #pics
子選擇符 #pics li
後代選擇符 .box a{}
通配選擇符 *
屬性選擇符 [href=」#pic」]
僞類和僞元素 a:hover
參考《高性能網站建設-進階指南》,有以下建議:
避免使用統配規則;
不要限定ID選擇符;
不要限定類選擇符;
讓規則越具體越好;
避免使用後代選擇符;
避免使用標籤-子選擇符;
質疑子選擇符的全部用途;
依靠繼承;
還要注意到,即使是頁面加載後,當頁面被觸發引發迴流(reflow)的時候,低效的選擇符依然會引起更高的開銷,顯然這對於用戶是不佳的體驗。
四、Javascript 的性能優化點
a、慎用Eval 謹記:有「eval」的代碼比沒有「eval」的代碼要慢上 100 倍以上。主要緣由是:JavaScript 代碼在執行前會進行相似「預編譯」的操做:首先會建立一個當前執行環境下的活動對象,並將那些用 var 申明的變量設置爲活動對象的屬性,可是此時這些變量的賦值都是 undefined,並將那些以 function 定義的函數也添加爲活動對象的屬性,並且它們的值正是函數的定義。可是,若是你使用了「eval」,則「eval」中的代碼(實際上爲字符串)沒法預先識 別其上下文,沒法被提早解析和優化,即沒法進行預編譯的操做。因此,其性能也會大幅度下降。
b、推薦儘可能使用局部變量 JavaScript 代碼解釋執行,在進入函數內部時,它會預先分析當前的變量,並將這些變量納入不一樣的層級(level),通常狀況下:
局部變量放入層級 1(淺),全局變量放入層級 2(深)。若是進入「with」或「try – catch」代碼塊,則會增長新的層級,即將「with」或「catch」裏的變量放入最淺層(層 1),並將以前的層級依次加深。變量所在的層越淺,訪問(讀取或修改)速度越快,尤爲是對於大量使用全局變量的函數裏面。
c、字符串數組方式拼接避免在IE6下的開銷
var tips = 'tip1'+'tip2';
這是咱們拼接字符串經常使用的方式,可是這種方式會有一些臨時變量的建立和銷燬,影響性能,尤爲是在IE6下,因此推薦使用以下方式拼接:
var tip_array = [],tips;
tip_array.push('tip1');
tip_array.push('tip2');
tips = tip_array.join('');
固然,最新的瀏覽器(如火狐 Firefox3+,IE8+ 等等)對字符串的拼接作了優化,性能略快於數組的「join」方法。
以上僅列出三種常見的優化方法,僅拋磚以引玉石,更多的javascript優化點,好比避免隱式類型轉換, 縮小對象訪問層級,利用變量優化字符串匹配等你們能夠繼續深刻挖掘;
五、DOM 操做優化
首先澄清兩個概念——Repaint 和 Reflow:Repaint 也叫 Redraw,它指的是一種不會影響當前 DOM 的結構和佈局的一種重繪動做。以下動做會產生 Repaint 動做:
不可見到可見(visibility 樣式屬性);
顏色或圖片變化(background, border-color, color 樣式屬性);
不改變頁面元素大小,形狀和位置,但改變其外觀的變化
Reflow 比起 Repaint 來說就是一種更加顯著的變化了。它主要發生在 DOM 樹被操做的時候,任何改變 DOM 的結構和佈局都會產生 Reflow。但一個元素的 Reflow 操做發生時,它的全部父元素和子元素都會放生 Reflow,最後 Reflow 必然會致使 Repaint 的產生。舉例說明,以下動做會產生 Reflow 動做:
瀏覽器窗口的變化;
DOM 節點的添加刪除操做
一些改變頁面元素大小,形狀和位置的操做的觸發經過 Reflow 和 Repaint 的介紹可知,每次 Reflow 比其 Repaint 會帶來更多的資源消耗,所以,咱們應該儘可能減小 Reflow 的發生,或者將其轉化爲只會觸發 Repaint 操做的代碼。
var tipBox = document.createElement('div');
document.body.appendChild('tipBox');//reflow
var tip1 = document.createElement('div');
var tip2 = document.createElement('div');
tipBox.appendChild(tip1);//reflow
tipBox.appendChild(tip2);//reflow
如上的代碼,會產生三次reflow,優化後的代碼以下:
var tipBox = document.createElement('div');
tip1 = document.createElement('div');
tip2 = document.createElement('div');
tipBox.appendChild(tip1);
tipBox.appendChild(tip2);
document.body.appendChild('tipBox');//reflow
固然還能夠利用 display 來減小reflow次數
var tipBox = document.getElementById('tipBox');
tipBox.style.display = 'none';//reflow
tipBox.appendChild(tip1);
tipBox.appendChild(tip2);
tipBox.appendChild(tip3);
tipBox.appendChild(tip4);
tipBox.appendChild(tip5);
tipBox.style.width = 120;
tipBox.style.height = 60;
tipBox.style.display = 'block';//reflow
DOM元素測量屬性和方法也會觸發reflow,以下:
var tipWidth = tipBox.offsetWidth;//reflow
tipScrollLeft = tipBox.scrollLeft;//reflow
display = window.getComputedStyle(div,'').getPropertyValue('display');//reflow
觸發reflow的屬性和方法大概有這些:
offsetLeft
offsetTop
offsetHeight
offsetWidth
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
getComputedStyle()
currentStyle(in IE))
咱們能夠用臨時變量將「offsetWidth」的值緩存起來,這樣就不用每次訪問「offsetWidth」屬性。這種方式在循環裏面很是適用,能夠極大地提升性能。
若是有批量的樣式屬性須要修改,建議經過替換className的方式來下降reflow的次數,曾經有這樣一個場景:有三個intput,分別對 應下面三個圖片和三個內容區域,第二input選中的時候,第二圖片顯示,其餘圖片隱藏,第二塊內容顯示,其餘內容隱藏,直接操做DOM節點的代碼以下
var input = [];
pics = [];
contents = [];
......
inputFrame.onclick =function(e){
var _e,_target;
_e = e ? window.event : null;
if(!_e){
return;
}else{
_target = _e.srcElement || _e.target ;
_index = getIndex(_target);//reflow兩次
show(_target,_index);//reflow兩次
}
}
function show(target,j){
for(var i = 0,i<3;i++){
target[i].style.display = 'none';//reflow
}
target[j].style.display = 'block';//reflow
}
function getIndex(targer){
if(target){
.....//獲取當前的元素索引
return index;
}
}
若是是經過css預先定義元素的隱藏和顯示,經過對父級的className進行操縱,將會把reflow的次數減小到1次
.pbox .pic,.pbox content{display:none}
.J_pbox_0 .pic0,.J_pbox_0 .content0{diplay:block}
.J_pbox_1 .pic1,.J_pbox_1 .content1{diplay:block}
.J_pbox_2 .pic2,.J_pbox_2 .content2{diplay:block}
var input = [],
parentBox = document.getELementById('J_Pbox');
......
for(var i = 0;i<3;i++){
input[i].onclick = function(e){
parentBox.className = 'pbox J_pbox_'+i;//reflow一次
}
}
3、Yahoo軍規再度挖掘會怎樣?
在網站性能優化的路上,是不會有終點的,這也是前端工程師永不會妥協的地方。
想看到更牛P的優化建議麼,請移步這裏來關注李牧童鞋的分享:
使用combo合併靜態資源
Bigpipe技術合併動態數據
Comet:基於http的服務端推技術
使用DataURI減小圖片請求
使用良好的JS,CSS版本管理方案
嘗試僅做必要的JS更新
利用本地存儲作緩存
關於最小化HTML
進一步討論Gzip
進一步討論域名劃分
打開keep-alive,重用HTTP鏈接
使用JSON進行數據交換
保障頁面可交互性
縮短最快可交互時間
異步無阻腳本下載
優化內存使用,防止內存泄露
高效的JavaScript
第三方代碼性能問題
Inline腳本不要與CSS穿插使用
使用高效的CSS選擇器
進一步討論及早Flush
關於視覺和心理學