再談Yahoo關(guān)于性能優(yōu)化的N條軍規(guī)
本來(lái)這是個(gè)老生常談的問(wèn)題,上周自成又分享了一些性能優(yōu)化的建議,我這里再做一個(gè)全面的Tips整理,謹(jǐn)作為查閱型的文檔,不妥之處,還請(qǐng)指正;
如果你已經(jīng)對(duì)yahoo這些優(yōu)化建議爛熟于心,果斷點(diǎn)這里
一、 Yahoo的軍規(guī)條例:
謹(jǐn)記:80%-90%的終端響應(yīng)時(shí)間是花費(fèi)在下載頁(yè)面中的圖片,樣式表,腳本,flash等;
詳細(xì)的解釋來(lái)這里查:http://developer.yahoo.com/performance/rules.html
也可以直接firebug上一項(xiàng)項(xiàng)比對(duì),如下圖:
簡(jiǎn)單翻譯解釋下:
1、盡量減少HTTP請(qǐng)求個(gè)數(shù)——須權(quán)衡
合并圖片(如css sprites,內(nèi)置圖片使用數(shù)據(jù))、合并CSS、JS,這一點(diǎn)很重要,但是要考慮合并后的文件體積。
2、使用CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))
這里可以關(guān)注CDN的三類(lèi)實(shí)現(xiàn):鏡像、高速緩存、專(zhuān)線,以及智能路由器和負(fù)載均衡;
3、為文件頭指定Expires或Cache-Control,使內(nèi)容具有緩存性。
區(qū)分靜態(tài)內(nèi)容和動(dòng)態(tài)內(nèi)容,避免以后頁(yè)面訪問(wèn)中不必要的HTTP請(qǐng)求。
4、避免空的src和href
留意具有這兩個(gè)屬性的標(biāo)簽如link,script,img,iframe等;
5、使用gzip壓縮內(nèi)容
Gzip壓縮所有可能的文件類(lèi)型以來(lái)減少文件體積
6、把CSS放到頂部
實(shí)現(xiàn)頁(yè)面有秩序地加載,這對(duì)于擁有較多內(nèi)容的頁(yè)面和網(wǎng)速較慢的用戶(hù)來(lái)說(shuō)更為重要,同時(shí),HTML規(guī)范清楚指出樣式表要放包含在頁(yè)面的<head />區(qū)域內(nèi);
7、把JS放到底部
HTTP/1.1 規(guī)范建議,瀏覽器每個(gè)主機(jī)名的并行下載內(nèi)容不超過(guò)兩個(gè),而問(wèn)題在于腳本阻止了頁(yè)面的平行下載,即便是主機(jī)名不相同
8、避免使用CSS表達(dá)式
頁(yè)面顯示和縮放,滾動(dòng)、乃至移動(dòng)鼠標(biāo)時(shí),CSS表達(dá)式的計(jì)算頻率是我們要關(guān)注的??梢钥紤]一次性的表達(dá)式或者使用事件句柄來(lái)代替CSS表達(dá)式。
9、將CSS和JS放到外部文件中
我們需要權(quán)衡內(nèi)置代碼帶來(lái)的HTTP請(qǐng)求減少與通過(guò)使用外部文件進(jìn)行緩存帶來(lái)的好處的折中點(diǎn)。
10、減少DNS查找次數(shù)
我們需要權(quán)衡減少 DNS查找次數(shù)和保持較高程度并行下載兩者之間的關(guān)系。
11、精簡(jiǎn)CSS和JS
目的就是減少下載的文件體積,可考慮壓縮工具JSMin和YUI Compressor。
12、避免跳轉(zhuǎn)
為了確?!昂笸恕卑粹o可以正確地使用,使用標(biāo)準(zhǔn)的 3XXHTTP狀態(tài)代碼;同域中注意避免反斜杠 “/” 的跳轉(zhuǎn);
跨域使用 Alias或者 mod_rewirte建立 CNAME(保存一個(gè)域名和另外一個(gè)域名之間關(guān)系的DNS記錄)
13、剔除重復(fù)的JS和CSS
重復(fù)調(diào)用腳本,除了增加額外的HTTP請(qǐng)求外,多次運(yùn)算也會(huì)浪費(fèi)時(shí)間。在IE和Firefox中不管腳本是否可緩存,它們都存在重復(fù)運(yùn)算JavaScript的問(wèn)題。
14、配置ETags
Entity tags(ETags)(實(shí)體標(biāo)簽)是web服務(wù)器和瀏覽器用于判斷瀏覽器緩存中的內(nèi)容和服務(wù)器中的原始內(nèi)容是否匹配的一種機(jī)制(“實(shí)體”就是所說(shuō)的“內(nèi) 容”,包括圖片、腳本、樣式表等),是比last-modified date更更加靈活的機(jī)制,單位時(shí)間內(nèi)文件被修過(guò)多次,Etag可以綜合Inode(文件的索引節(jié)點(diǎn)(inode)數(shù)),MTime(修改時(shí)間)和Size來(lái)精準(zhǔn)的進(jìn)行判斷,避開(kāi)UNIX記錄MTime只能精確到秒的問(wèn)題。 服務(wù)器集群使用,可取后兩個(gè)參數(shù)。使用ETags減少Web應(yīng)用帶寬和負(fù)載。
15、使AJAX可緩存
利用時(shí)間戳,更精巧的實(shí)現(xiàn)響應(yīng)可緩存與服務(wù)器數(shù)據(jù)同步更新。
16、盡早刷新輸出緩沖
尤其對(duì)于css,js文件的并行下載更有意義
17、使用GET來(lái)完成AJAX請(qǐng)求
當(dāng)使用XMLHttpRequest時(shí),瀏覽器中的POST方法是一個(gè)“兩步走”的過(guò)程:首先發(fā)送文件頭,然后才發(fā)送數(shù)據(jù)。在url小于2K時(shí)使用GET獲取數(shù)據(jù)時(shí)更加有意義。
18、延遲加載
確定頁(yè)面運(yùn)行正常后,再加載腳本來(lái)實(shí)現(xiàn)如拖放和動(dòng)畫(huà),或者是隱藏部分的內(nèi)容以及折疊內(nèi)容等。
19、預(yù)加載
關(guān)注下無(wú)條件加載,有條件加載和有預(yù)期的加載。
20、減少DOM元素個(gè)數(shù)
使用更適合或者在語(yǔ)意是更貼切的標(biāo)簽,要考慮大量DOM元素中循環(huán)的性能開(kāi)銷(xiāo)。
21、根據(jù)域名劃分頁(yè)面內(nèi)容
很顯然, 是最大限度地實(shí)現(xiàn)平行下載
22、盡量減少iframe的個(gè)數(shù)
考慮即使內(nèi)容為空,加載也需要時(shí)間,會(huì)阻止頁(yè)面加載,沒(méi)有語(yǔ)意,注意iframe相對(duì)于其他DOM元素高出1-2個(gè)數(shù)量級(jí)的開(kāi)銷(xiāo),它會(huì)在典型方式下阻塞onload事件,IE和Firefox中主頁(yè)面樣式表會(huì)阻塞它的下載。
23、避免404
HTTP請(qǐng)求時(shí)間消耗是很大的,有些站點(diǎn)把404錯(cuò)誤響應(yīng)頁(yè)面改為“你是不是要找***”,這雖然改進(jìn)了用戶(hù)體驗(yàn)但是同樣也會(huì)浪費(fèi)服務(wù)器資源(如數(shù)據(jù)庫(kù)等)。最糟糕的情況是指向外部 JavaScript的鏈接出現(xiàn)問(wèn)題并返回404代碼。首先,這種加載會(huì)破壞并行加載;其次瀏覽器會(huì)把試圖在返回的404響應(yīng)內(nèi)容中找到可能有用的部分當(dāng)作JavaScript代碼來(lái)執(zhí)行。
24、減少Cookie的大小
去除不必要的coockie
使coockie體積盡量小以減少對(duì)用戶(hù)響應(yīng)的影響
注意在適應(yīng)級(jí)別的域名上設(shè)置coockie以便使子域名不受影響
設(shè)置合理的過(guò)期時(shí)間。較早地Expire時(shí)間和不要過(guò)早去清除coockie,都會(huì)改善用戶(hù)的響應(yīng)時(shí)間。
25、使用無(wú)cookie的域
確定對(duì)于靜態(tài)內(nèi)容的請(qǐng)求是無(wú)coockie的請(qǐng)求。創(chuàng)建一個(gè)子域名并用他來(lái)存放所有靜態(tài)內(nèi)容。
26、減少DOM訪問(wèn)
緩存已經(jīng)訪問(wèn)過(guò)的有關(guān)元素
線下更新完節(jié)點(diǎn)之后再將它們添加到文檔樹(shù)中
避免使用JavaScript來(lái)修改頁(yè)面布局
27、開(kāi)發(fā)智能事件處理程序
有時(shí)候我們會(huì)感覺(jué)到頁(yè)面反應(yīng)遲鈍,這是因?yàn)镈OM樹(shù)元素中附加了過(guò)多的事件句柄并且些事件句病被頻繁地觸發(fā)。這就是為什么說(shuō)使用event delegation(事件代理)是一種好方法了。如果你在一個(gè)div中有10個(gè)按鈕,你只需要在div上附加一次事件句柄就可以了,而不用去為每一個(gè)按 鈕增加一個(gè)句柄。事件冒泡時(shí)你可以捕捉到事件并判斷出是哪個(gè)事件發(fā)出的。
你同樣也不用為了操作DOM樹(shù)而等待onload事件的發(fā)生。你需要做的就是等待樹(shù)結(jié)構(gòu)中你要訪問(wèn)的元素出現(xiàn)。你也不用等待所有圖像都加載完畢。
你可能會(huì)希望用DOMContentLoaded事件來(lái)代替 事件應(yīng)用程序中的onAvailable方法。
28、用<link>代替@import
在IE中,頁(yè)面底部@import和使用<link>作用是一樣的,因此最好不要使用它。
29、避免使用濾鏡
完全避免使用AlphaImageLoader的最好方法就是使用PNG8格式來(lái)代替,這種格式能在IE中很好地工作。如果你確實(shí)需要使用 AlphaImageLoader,請(qǐng)使用下劃線_filter又使之對(duì)IE7以上版本的用戶(hù)無(wú)效。
30、優(yōu)化圖像
嘗試把GIF格式轉(zhuǎn)換成PNG格式,看看是否節(jié)省空間。在所有的PNG圖片上運(yùn)行pngcrush(或者其它PNG優(yōu)化工具)
31、優(yōu)化CSS Spirite
在Spirite中水平排列你的圖片,垂直排列會(huì)稍稍增加文件大?。?br />
Spirite中把顏色較近的組合在一起可以降低顏色數(shù),理想狀況是低于256色以便適用PNG8格式;
便于移動(dòng),不要在Spirite的圖像中間留有較大空隙。這雖然不大會(huì)增加文件大小但對(duì)于用戶(hù)代理來(lái)說(shuō)它需要更少的內(nèi)存來(lái)把圖片解壓為像素地圖。 100×100的圖片為1萬(wàn)像素,而1000×1000就是100萬(wàn)像素。
32、不要在HTML中縮放圖像——須權(quán)衡
不要為了在HTML中設(shè)置長(zhǎng)寬而使用比實(shí)際需要大的圖片。如果你需要:
<img width=”100″ height=”100″ src=”mycat.jpg” alt=”My Cat” />
那么你的圖片(mycat.jpg)就應(yīng)該是100×100像素而不是把一個(gè)500×500像素的圖片縮小使用。這里在下文有更有趣的分析。
33、favicon.ico要小而且可緩存
favicon.ico是位于服務(wù)器根目錄下的一個(gè)圖片文件。它是必定存在的,因?yàn)榧词鼓悴魂P(guān)心它是否有用,瀏覽器也會(huì)對(duì)它發(fā)出請(qǐng)求,因此最好不要返回一 個(gè)404 Not Found的響應(yīng)。由于是在同一臺(tái)服務(wù)器上,它每被請(qǐng)求一次coockie就會(huì)被發(fā)送一次。這個(gè)圖片文件還會(huì)影響下載順序,例如在IE中當(dāng)你在 onload中請(qǐng)求額外的文件時(shí),favicon會(huì)在這些額外內(nèi)容被加載前下載。
因此,為了減少favicon.ico帶來(lái)的弊端,要做到:
文件盡量地小,最好小于1K
在適當(dāng)?shù)臅r(shí)候(也就是你不要打算再換favicon.ico的時(shí)候,因?yàn)楦鼡Q新文件時(shí)不能對(duì)它進(jìn)行重命名)為它設(shè)置Expires文件頭。你可以很安全地 把Expires文件頭設(shè)置為未來(lái)的幾個(gè)月。你可以通過(guò)核對(duì)當(dāng)前favicon.ico的上次編輯時(shí)間來(lái)作出判斷。
Imagemagick可以幫你創(chuàng)建小巧的favicon。
34、保持單個(gè)內(nèi)容小于25K
因?yàn)閕Phone不能緩存大于25K的文件。注意這里指的是解壓縮后的大小。由于單純gizp壓縮可能達(dá)不要求,因此精簡(jiǎn)文件就顯得十分重 要。
35、打包組件成復(fù)合文本
頁(yè)面內(nèi)容打包成復(fù)合文本就如同帶有多附件的Email,它能夠使你在一個(gè)HTTP請(qǐng)求中取得多個(gè)組件(切記:HTTP請(qǐng)求是很奢侈的)。當(dāng)你使用這條規(guī) 則時(shí),首先要確定用戶(hù)代理是否支持(iPhone就不支持)。
二、Yahoo軍規(guī)之外的場(chǎng)景?
1、 使用json作為數(shù)據(jù)的交換格式
Json在瀏覽器解析的效率至少高于XML一個(gè)數(shù)量級(jí),高級(jí)瀏覽器中內(nèi)置的有生成和解析json的方法,IE6中要用額外的方法(http://json.org),不要用eval,容易引發(fā)性能和安全問(wèn)題。
2、 盡可能對(duì)images和table設(shè)定寬高值
針對(duì)Yslow的不要在HTML中縮放圖像——第33條,有人會(huì)誤解為不要對(duì)圖片加寬高值,其實(shí)這條建議本身的意思是不要為了獲取一個(gè)特定大小的圖片,而去強(qiáng)行通過(guò)設(shè)置寬高值拉伸或者壓縮一個(gè)既有的圖片。建議是另存一張符合尺寸的圖片替代。
對(duì)圖片和table是設(shè)定寬高,是考慮到如果瀏覽器能立刻知道圖片或者tables的寬高,它就能夠直接呈現(xiàn)頁(yè)面而不需要通過(guò)計(jì)算元素大小后重繪,而且即便是圖片損毀而沒(méi)有展現(xiàn),也不會(huì)進(jìn)而破壞了頁(yè)面本來(lái)的布局。
有一些應(yīng)用場(chǎng)景需要注意:
- a、批量圖片,圖片源可控同時(shí)頁(yè)面圖片寬高值不可變,比如數(shù)據(jù)庫(kù)有100張100*100的圖片要在頁(yè)面中全部展示,那么建議是都寫(xiě)上
<img width=”100″ height=”120″ src=”” alt=”” />
- b、批量圖片,圖片源不可控同時(shí)頁(yè)面圖片寬高值不可變,比如數(shù)據(jù)庫(kù)有100張圖片,而已知圖片有的尺寸是97*100,有的100*105,而又不可能去一張張修改另存。這里視情況而定,根據(jù)圖片尺寸與要求尺寸的偏離度,在保證圖片不拉伸變形同時(shí)不影響頁(yè)面布局的情況下,可以對(duì)圖片單獨(dú)設(shè)定寬度100,同時(shí)對(duì)其包裹的容器設(shè)定100*100的寬高來(lái)隱藏多出來(lái)的部分,注意不能同時(shí)設(shè)置寬高以防止變形。
- c、批量圖片,圖片源不可控,頁(yè)面圖片寬高值不定,比如數(shù)據(jù)庫(kù)有100張各種尺寸偏差較大的,此時(shí)可不對(duì)圖片設(shè)置寬高;
其他情況不一一羅列,原則是在最大程度保證圖片不變形與圖片最大面積展現(xiàn)的前提下,盡可能為圖片設(shè)置寬高值,總之就是權(quán)衡。
Tables的寬高值同圖片,盡可能設(shè)置。
3、 拆離內(nèi)容塊
盡量用div取代tables,或者將tables打破成嵌套層次深的結(jié)構(gòu);
避免用這樣的嵌套
<table> <table> <table> ... </table> </table> </table>
采用下面的或者div重構(gòu):
<table></table> <table></table> <table></table>
4、 高效的CSS書(shū)寫(xiě)規(guī)則
眾所周知,CSS選擇符是從右向左進(jìn)行匹配的。
通常一個(gè)圖片列表的的小模塊
<div id="box"> <div> <h3>我的旅途</h3> </div> <div> <h4>旅途1</h4> <ul id="pics"> <li> <a href="#pic" title=""><img src="" alt="" /> </a> <p>這是在<strong>圖片1</strong></p> </li> </ul> </div> </div>
為了代碼上縮進(jìn)后內(nèi)層的整潔性,我們html有可能這樣寫(xiě)之外,更喜歡看這樣的css寫(xiě)法:
.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}
其實(shí)寫(xiě)到這里,問(wèn)題已經(jīng)顯而易見(jiàn)了。深達(dá)五層抑或六層的嵌套,同時(shí)右邊的選擇符都是采用標(biāo)簽,在滿(mǎn)足我們視覺(jué)平整與代碼結(jié)構(gòu)系統(tǒng)化的時(shí)候,付出的是性能的代價(jià)。
不做進(jìn)一步的代碼書(shū)寫(xiě)方式的探討,受個(gè)人習(xí)慣與應(yīng)用場(chǎng)景影響。這里對(duì)css選擇符按照開(kāi)銷(xiāo)從小到大的順序梳理一下:
參考《高性能網(wǎng)站建設(shè)-進(jìn)階指南》,有如下建議:
- 避免使用統(tǒng)配規(guī)則;
- 不要限定ID選擇符;
- 不要限定類(lèi)選擇符;
- 讓規(guī)則越具體越好;
- 避免使用后代選擇符;
- 避免使用標(biāo)簽-子選擇符;
- 質(zhì)疑子選擇符的所有用途;
- 依靠繼承;
還要注意到,即便是頁(yè)面加載后,當(dāng)頁(yè)面被觸發(fā)引起回流(reflow)的時(shí)候,低效的選擇符依然會(huì)引發(fā)更高的開(kāi)銷(xiāo),顯然這對(duì)于用戶(hù)是不佳的體驗(yàn)。
4、Javascript 的性能優(yōu)化點(diǎn)
- a、慎用Eval
謹(jǐn)記:有“eval”的代碼比沒(méi)有“eval”的代碼要慢上 100 倍以上。主要原因是:JavaScript 代碼在執(zhí)行前會(huì)進(jìn)行類(lèi)似“預(yù)編譯”的操作:首先會(huì)創(chuàng)建一個(gè)當(dāng)前執(zhí)行環(huán)境下的活動(dòng)對(duì)象,并將那些用 var 申明的變量設(shè)置為活動(dòng)對(duì)象的屬性,但是此時(shí)這些變量的賦值都是 undefined,并將那些以 function 定義的函數(shù)也添加為活動(dòng)對(duì)象的屬性,而且它們的值正是函數(shù)的定義。但是,如果你使用了“eval”,則“eval”中的代碼(實(shí)際上為字符串)無(wú)法預(yù)先識(shí)別其上下文,無(wú)法被提前解析和優(yōu)化,即無(wú)法進(jìn)行預(yù)編譯的操作。所以,其性能也會(huì)大幅度降低。
- b、推薦盡量使用局部變量
JavaScript 代碼解釋執(zhí)行,在進(jìn)入函數(shù)內(nèi)部時(shí),它會(huì)預(yù)先分析當(dāng)前的變量,并將這些變量歸入不同的層級(jí)(level),一般情況下:
局部變量放入層級(jí) 1(淺),全局變量放入層級(jí) 2(深)。如果進(jìn)入“with”或“try – catch”代碼塊,則會(huì)增加新的層級(jí),即將“with”或“catch”里的變量放入最淺層(層 1),并將之前的層級(jí)依次加深。變量所在的層越淺,訪問(wèn)(讀取或修改)速度越快,尤其是對(duì)于大量使用全局變量的函數(shù)里面。 - c、字符串?dāng)?shù)組方式拼接避免在IE6下的開(kāi)銷(xiāo)
var tips = 'tip1'+'tip2';
這是我們拼接字符串常用的方式,但是這種方式會(huì)有一些臨時(shí)變量的創(chuàng)建和銷(xiāo)毀,影響性能,尤其是在IE6下,所以推薦使用如下方式拼接:
var tip_array = [],tips; tip_array.push('tip1'); tip_array.push('tip2'); tips = tip_array.join('');
當(dāng)然,最新的瀏覽器(如火狐 Firefox3+,IE8+ 等等)對(duì)字符串的拼接做了優(yōu)化,性能略快于數(shù)組的“join”方法。
- 以上僅列出三種常見(jiàn)的優(yōu)化方法,僅拋磚以引玉石,更多的javascript優(yōu)化點(diǎn),比如避免隱式類(lèi)型轉(zhuǎn)換, 縮小對(duì)象訪問(wèn)層級(jí),利用變量?jī)?yōu)化字符串匹配等大家可以繼續(xù)深入挖掘;
5、DOM 操作優(yōu)化
首先澄清兩個(gè)概念——Repaint 和 Reflow:Repaint 也叫 Redraw,它指的是一種不會(huì)影響當(dāng)前 DOM 的結(jié)構(gòu)和布局的一種重繪動(dòng)作。如下動(dòng)作會(huì)產(chǎn)生 Repaint 動(dòng)作:
- 不可見(jiàn)到可見(jiàn)(visibility 樣式屬性);
- 顏色或圖片變化(background, border-color, color 樣式屬性);
- 不改變頁(yè)面元素大小,形狀和位置,但改變其外觀的變化
Reflow 比起 Repaint 來(lái)講就是一種更加顯著的變化了。它主要發(fā)生在 DOM 樹(shù)被操作的時(shí)候,任何改變 DOM 的結(jié)構(gòu)和布局都會(huì)產(chǎn)生 Reflow。但一個(gè)元素的 Reflow 操作發(fā)生時(shí),它的所有父元素和子元素都會(huì)放生 Reflow,最后 Reflow 必然會(huì)導(dǎo)致 Repaint 的產(chǎn)生。舉例說(shuō)明,如下動(dòng)作會(huì)產(chǎn)生 Reflow 動(dòng)作:
- 瀏覽器窗口的變化;
- DOM 節(jié)點(diǎn)的添加刪除操作
- 一些改變頁(yè)面元素大小,形狀和位置的操作的觸發(fā)
- offsetLeft
- offsetTop
- offsetHeight
- offsetWidth
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
- getComputedStyle()
- currentStyle(in IE))
- 使用combo合并靜態(tài)資源
- Bigpipe技術(shù)合并動(dòng)態(tài)數(shù)據(jù)
- Comet:基于http的服務(wù)端推技術(shù)
- 使用DataURI減少圖片請(qǐng)求
- 使用良好的JS,CSS版本管理方案
- 嘗試僅作必要的JS更新
- 利用本地存儲(chǔ)做緩存
- 關(guān)于最小化HTML
- 進(jìn)一步討論Gzip
- 進(jìn)一步討論域名劃分
- 打開(kāi)keep-alive,重用HTTP連接
- 使用JSON進(jìn)行數(shù)據(jù)交換
- 保障頁(yè)面可交互性
- 縮短最快可交互時(shí)間
- 異步無(wú)阻腳本下載
- 優(yōu)化內(nèi)存使用,防止內(nèi)存泄露
- 高效的JavaScript
- 第三方代碼性能問(wèn)題
- Inline腳本不要與CSS穿插使用
- 使用高效的CSS選擇器
- 進(jìn)一步討論及早Flush
- 關(guān)于視覺(jué)和心理學(xué)
通過(guò) Reflow 和 Repaint 的介紹可知,每次 Reflow 比其 Repaint 會(huì)帶來(lái)更多的資源消耗,因此,我們應(yīng)該盡量減少 Reflow 的發(fā)生,或者將其轉(zhuǎn)化為只會(huì)觸發(fā) 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
如上的代碼,會(huì)產(chǎn)生三次reflow,優(yōu)化后的代碼如下:
var tipBox = document.createElement('div'); tip1 = document.createElement('div'); tip2 = document.createElement('div'); tipBox.appendChild(tip1); tipBox.appendChild(tip2); document.body.appendChild('tipBox');//reflow
當(dāng)然還可以利用 display 來(lái)減少reflow次數(shù)
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元素測(cè)量屬性和方法也會(huì)觸發(fā)reflow,如下:
var tipWidth = tipBox.offsetWidth;//reflow tipScrollLeft = tipBox.scrollLeft;//reflow display = window.getComputedStyle(div,'').getPropertyValue('display');//reflow
觸發(fā)reflow的屬性和方法大概有這些:
我們可以用臨時(shí)變量將“offsetWidth”的值緩存起來(lái),這樣就不用每次訪問(wèn)“offsetWidth”屬性。這種方式在循環(huán)里面非常適用,可以極大地提高性能。
如果有批量的樣式屬性需要修改,建議通過(guò)替換className的方式來(lái)降低reflow的次數(shù),曾經(jīng)有這樣一個(gè)場(chǎng)景:有三個(gè)intput,分別對(duì)應(yīng)下面三個(gè)圖片和三個(gè)內(nèi)容區(qū)域,第二input選中的時(shí)候,第二圖片顯示,其他圖片隱藏,第二塊內(nèi)容顯示,其他內(nèi)容隱藏,直接操作DOM節(jié)點(diǎn)的代碼如下
var input = []; pics = []; contents = []; ...... for(var i = 0;i<3;i++){ input[i].onclick = function(e){ show(pics,i);//reflow兩次 show(contents,i);//reflow兩次 } } function show(target,j){ for(var i = 0,i<3;i++){ target[i].style.display = 'none';//reflow } target[j].style.display = 'block';//reflow }
如果是通過(guò)css預(yù)先定義元素的隱藏和顯示,通過(guò)對(duì)父級(jí)的className進(jìn)行操縱,將會(huì)把reflow的次數(shù)減少到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一次 } }
三、Yahoo軍規(guī)再度挖掘會(huì)怎樣?
在網(wǎng)站性能優(yōu)化的路上,是不會(huì)有終點(diǎn)的,這也是前端工程師永不會(huì)妥協(xié)的地方。
想看到更牛P的優(yōu)化建議么,請(qǐng)移步這里來(lái)關(guān)注李牧童鞋的分享:
來(lái)源:http://ued.alimama.com/front-end/quick-tips-among-yahoo-n-rules/
- 目前還沒(méi)評(píng)論,等你發(fā)揮!