1. Javascript
(1). DOM
DOM操作應(yīng)該是腳本中耗性能的一類操作,例如增加、修改、刪除 DOM元素或者對(duì) DOM集合進(jìn)行操作。如果腳本中包含了大量的 DOM操作則需要注意以下幾點(diǎn):
a. HTML Collection(HTML收集器,返回的是一個(gè)數(shù)組內(nèi)容信息)
在腳本中 document.images、document.forms 、getElementsByTagName()返回的都是 HTMLCollection類型的集合,在平時(shí)使用的時(shí)候大多將它作為數(shù)組來使用,因?yàn)樗?length屬性,也可以使用索引訪問每一個(gè)元素。不過在訪問性能上則比數(shù)組要差很多,原因是這個(gè)集合并不是一個(gè)靜態(tài)的結(jié)果,它表示的僅僅是一個(gè)特定的查詢,每次訪問該集合時(shí)都會(huì)重新執(zhí)行這個(gè)查詢從而更新查詢結(jié)果。所謂的 “訪問集合” 包括讀取集合的 length屬性、訪問集合中的元素。
因此,當(dāng)你需要遍歷 HTML Collection的時(shí)候,盡量將它轉(zhuǎn)為數(shù)組后再訪問,以提高性能。即使不轉(zhuǎn)換為數(shù)組,也請(qǐng)盡可能少的訪問它,例如在遍歷的時(shí)候可以將 length屬性、成員保存到局部變量后再使用局部變量。
b. Reflow & Repaint
除了上面一點(diǎn)之外, DOM操作還需要考慮瀏覽器的 Reflow和Repaint ,因?yàn)檫@些都是需要消耗資源的,具體的可以參加以下文章:
如何減少瀏覽器的repaint和reflow?
Understanding Internet Explorer Rendering Behaviour
Notes on HTML Reflow
(2). 慎用 with
with(obj){ p = 1}; 代碼塊的行為實(shí)際上是修改了代碼塊中的 執(zhí)行環(huán)境 ,將obj放在了其作用域鏈的前端,在 with代碼塊中訪問非局部變量是都是先從 obj上開始查找,如果沒有再依次按作用域鏈向上查找,因此使用 with相當(dāng)于增加了作用域鏈長(zhǎng)度。而每次查找作用域鏈都是要消耗時(shí)間的,過長(zhǎng)的作用域鏈會(huì)導(dǎo)致查找性能下降。
因此,除非你能肯定在 with代碼中只訪問 obj中的屬性,否則慎用 with,替代的可以使用局部變量緩存需要訪問的屬性。
(3). 避免使用 eval和 Function
每次 eval 或 Function 構(gòu)造函數(shù)作用于字符串表示的源代碼時(shí),腳本引擎都需要將源代碼轉(zhuǎn)換成可執(zhí)行代碼。這是很消耗資源的操作 —— 通常比簡(jiǎn)單的函數(shù)調(diào)用慢 100倍以上。
eval 函數(shù)效率特別低,由于事先無法知曉傳給 eval 的字符串中的內(nèi)容,eval在其上下文中解釋要處理的代碼,也就是說編譯器無法優(yōu)化上下文,因此只能有瀏覽器在運(yùn)行時(shí)解釋代碼。這對(duì)性能影響很大。
Function 構(gòu)造函數(shù)比 eval略好,因?yàn)槭褂么舜a不會(huì)影響周圍代碼 ;但其速度仍很慢。
此外,使用 eval和 Function也不利于Javascript 壓縮工具執(zhí)行壓縮。
(4). 減少作用域鏈查找(這方面設(shè)計(jì)到一些內(nèi)容的相關(guān)問題)
前文談到了作用域鏈查找問題,這一點(diǎn)在循環(huán)中是尤其需要注意的問題。如果在循環(huán)中需要訪問非本作用域下的變量時(shí)請(qǐng)?jiān)诒闅v之前用局部變量緩存該變量,并在遍歷結(jié)束后再重寫那個(gè)變量,這一點(diǎn)對(duì)全局變量尤其重要,因?yàn)槿肿兞刻幱谧饔糜蜴湹捻敹耍L問時(shí)的查找次數(shù)是多的。
低效率的寫法:
// 全局變量 var globalVar = 1; function myCallback(info){ for( var i = 100000; i--;){ //每次訪問 globalVar 都需要查找到作用域鏈頂端,本例中需要訪問 100000 次 globalVar += i; } }
更高效的寫法:
// 全局變量 var globalVar = 1; function myCallback(info){ //局部變量緩存全局變量 var localVar = globalVar; for( var i = 100000; i--;){ //訪問局部變量是快的 localVar += i; } //本例中只需要訪問 2次全局變量 在函數(shù)中只需要將 globalVar中內(nèi)容的值賦給localVar 中區(qū) globalVar = localVar; }
此外,要減少作用域鏈查找還應(yīng)該減少閉包的使用。
(5). 數(shù)據(jù)訪問
Javascript中的數(shù)據(jù)訪問包括直接量 (字符串、正則表達(dá)式 )、變量、對(duì)象屬性以及數(shù)組,其中對(duì)直接量和局部變量的訪問是快的,對(duì)對(duì)象屬性以及數(shù)組的訪問需要更大的開銷。當(dāng)出現(xiàn)以下情況時(shí),建議將數(shù)據(jù)放入局部變量:
a. 對(duì)任何對(duì)象屬性的訪問超過 1次
b. 對(duì)任何數(shù)組成員的訪問次數(shù)超過 1次
另外,還應(yīng)當(dāng)盡可能的減少對(duì)對(duì)象以及數(shù)組深度查找。
(6). 字符串拼接
在 Javascript中使用"+" 號(hào)來拼接字符串效率是比較低的,因?yàn)槊看芜\(yùn)行都會(huì)開辟新的內(nèi)存并生成新的字符串變量,然后將拼接結(jié)果賦值給新變量。與之相比更為高效的做法是使用數(shù)組的 join方法,即將需要拼接的字符串放在數(shù)組中調(diào)用其 join方法得到結(jié)果。不過由于使用數(shù)組也有一定的開銷,因此當(dāng)需要拼接的字符串較多的時(shí)候可以考慮用此方法。
關(guān)于 Javascript優(yōu)化的更詳細(xì)介紹請(qǐng)參考:
Write Efficient Javascript(PPT)
Efficient JavaScript
2. CSS選擇符
在大多數(shù)人的觀念中,都覺得瀏覽器對(duì) CSS選擇符的解析式從左往右進(jìn)行的,例如
#toc A { color: #444; }
這樣一個(gè)選擇符,如果是從右往左解析則效率會(huì)很高,因?yàn)閭€(gè) ID選擇基本上就把查找的范圍限定了,但實(shí)際上瀏覽器對(duì)選擇符的解析是從右往左進(jìn)行的。如上面的選擇符,瀏覽器必須遍歷查找每一個(gè) A標(biāo)簽的祖先節(jié)點(diǎn),效率并不像之前想象的那樣高。根據(jù)瀏覽器的這一行為特點(diǎn),在寫選擇符的時(shí)候需要注意很多事項(xiàng),有人已經(jīng)一一列舉了, 詳情參考此處。
3. HTML
對(duì) HTML本身的優(yōu)化現(xiàn)如今也越來越多的受人關(guān)注了,詳情可以參見這篇 總結(jié)性文章 。
4. Image壓縮
圖片壓縮是個(gè)技術(shù)活,不過現(xiàn)如今這方面的工具也非常多,壓縮之后往往能帶來不錯(cuò)的效果,具體的壓縮原理以及方法在《 Even Faster Web Sites》第10 章有很詳細(xì)的介紹,有興趣的可以去看看。
總結(jié)
本文從頁(yè)面級(jí)以及代碼級(jí)兩個(gè)粒度對(duì)前端優(yōu)化的各種方式做了一個(gè)總結(jié),這些方法基本上都是前端開發(fā)人員在開發(fā)的過程中可以借鑒和實(shí)踐的,除此之外,完整的前端優(yōu)化還應(yīng)該包括很多其他的途徑,例如 CDN、 Gzip、多域名、無 Cookie服務(wù)器等等,由于對(duì)于開發(fā)人員的可操作性并不強(qiáng)大,在此也就不多敘述了,詳細(xì)的可以參考 Yahoo和Google 的這些“金科玉律。