前段時(shí)間簡(jiǎn)單的研究了下前端優(yōu)化相關(guān)的知識(shí),本文算是一個(gè)階段性的總結(jié),或者當(dāng)做一個(gè)優(yōu)化的參考。
前言
前端是龐大的,包括HTML、CSS、Javascript、Image、Flash等等各種各樣的資源。前端優(yōu)化是復(fù)雜的,針對(duì)方方面面的資源都有不同的方式。那么,前端優(yōu)化的目的是什么?
1. 從用戶角度而言,優(yōu)化能夠讓頁(yè)面加載得更快、對(duì)用戶的操作響應(yīng)得更及時(shí),能夠給用戶提供更為友好的體驗(yàn)。
2. 從服務(wù)商角度而言,優(yōu)化能夠減少頁(yè)面請(qǐng)求數(shù)、或者減小請(qǐng)求所占帶寬,能夠節(jié)省可觀的資源。
總之,恰當(dāng)?shù)膬?yōu)化不僅能夠改善站點(diǎn)的用戶體驗(yàn)并且能夠節(jié)省相當(dāng)?shù)馁Y源利用。
前端優(yōu)化的途徑有很多,按粒度大致可以分為兩類(lèi),第一類(lèi)是頁(yè)面級(jí)別的優(yōu)化,例如HTTP請(qǐng)求數(shù)、腳本的無(wú)阻塞加載、內(nèi)聯(lián)腳本的位置優(yōu)化等;第二類(lèi)則是代碼級(jí)別的優(yōu)化,例如Javascript中的DOM操作優(yōu)化、CSS選擇符優(yōu)化、圖片優(yōu)化以及HTML結(jié)構(gòu)優(yōu)化等等。另外,本著提高投入產(chǎn)出比的目的,后文提到的各種優(yōu)化策略大致按照投入產(chǎn)出比從大到小的順序排列。
一、頁(yè)面級(jí)優(yōu)化1. 減少HTTP請(qǐng)求數(shù)
這條策略基本上所有前端人都知道,而且也是最重要最有效的。都說(shuō)要減少HTTP請(qǐng)求,那請(qǐng)求多了到底會(huì)怎么樣呢?首先,每個(gè)請(qǐng)求都是有成本的,既包含時(shí)間成本也包含資源成本。一個(gè)完整的請(qǐng)求都需要經(jīng)過(guò)DNS尋址、與服務(wù)器建立連接、發(fā)送數(shù)據(jù)、等待服務(wù)器響應(yīng)、接收數(shù)據(jù)這樣一個(gè)“漫長(zhǎng)”而復(fù)雜的過(guò)程。時(shí)間成本就是用戶需要看到或者“感受”到這個(gè)資源是必須要等待這個(gè)過(guò)程結(jié)束的,資源上由于每個(gè)請(qǐng)求都需要攜帶數(shù)據(jù),因此每個(gè)請(qǐng)求都需要占用帶寬。另外,由于瀏覽器進(jìn)行并發(fā)請(qǐng)求的請(qǐng)求數(shù)是有上限的(具體參見(jiàn)此處),因此請(qǐng)求數(shù)多了以后,瀏覽器需要分批進(jìn)行請(qǐng)求,因此會(huì)增加用戶的等待時(shí)間,會(huì)給用戶造成站點(diǎn)速度慢這樣一個(gè)印象,即使可能用戶能看到的第一屏的資源都已經(jīng)請(qǐng)求完了,但是瀏覽器的進(jìn)度條會(huì)一直存在。
減少HTTP請(qǐng)求數(shù)的主要途徑包括:
(1). 從設(shè)計(jì)實(shí)現(xiàn)層面簡(jiǎn)化頁(yè)面
如果你的頁(yè)面像百度首頁(yè)一樣簡(jiǎn)單,那么接下來(lái)的規(guī)則基本上都用不著了。保持頁(yè)面簡(jiǎn)潔、減少資源的使用時(shí)最直接的。如果不是這樣,你的頁(yè)面需要華麗的皮膚,則繼續(xù)閱讀下面的內(nèi)容。
(2). 合理設(shè)置HTTP緩存
緩存的力量是強(qiáng)大的,恰當(dāng)?shù)木彺嬖O(shè)置可以大大的減少HTTP請(qǐng)求。以有啊首頁(yè)為例,當(dāng)瀏覽器沒(méi)有緩存的時(shí)候訪問(wèn)一共會(huì)發(fā)出78個(gè)請(qǐng)求,共600多K數(shù)據(jù)(如圖1.1),而當(dāng)?shù)诙卧L問(wèn)即瀏覽器已緩存之后訪問(wèn)則僅有10個(gè)請(qǐng)求,共20多K數(shù)據(jù)(如圖1.2)。(這里需要說(shuō)明的是,如果直接F5刷新頁(yè)面的話效果是不一樣的,這種情況下請(qǐng)求數(shù)還是一樣,不過(guò)被緩存資源的請(qǐng)求服務(wù)器是304響應(yīng),只有Header沒(méi)有Body,可以節(jié)省帶寬)
怎樣才算合理設(shè)置?原則很簡(jiǎn)單,能緩存越多越好,能緩存越久越好。例如,很少變化的圖片資源可以直接通過(guò)HTTP Header中的Expires設(shè)置一個(gè)很長(zhǎng)的過(guò)期頭;變化不頻繁而又可能會(huì)變的資源可以使用Last-Modifed來(lái)做請(qǐng)求驗(yàn)證。盡可能的讓資源能夠在緩存中待得更久。關(guān)于HTTP緩存的具體設(shè)置和原理此處就不再詳述了,有興趣的可以參考下列文章:
HTTP1.1協(xié)議中關(guān)于緩存策略的描述
Fiddler HTTP Performance中關(guān)于緩存的介紹
(3). 資源合并與壓縮
如果可以的話,盡可能的將外部的腳本、樣式進(jìn)行合并,多個(gè)合為一個(gè)。另外,CSS、Javascript、Image都可以用相應(yīng)的工具進(jìn)行壓縮,壓縮后往往能省下不少空間。
(4). CSS Sprites
合并CSS圖片,減少請(qǐng)求數(shù)的又一個(gè)好辦法。
(5). Inline Images
使用data: URL scheme的方式將圖片嵌入到頁(yè)面或CSS中,如果不考慮資源管理上的問(wèn)題的話,不失為一個(gè)好辦法。如果是嵌入頁(yè)面的話換來(lái)的是增大了頁(yè)面的體積,而且無(wú)法利用瀏覽器緩存。使用在CSS中的圖片則更為理想一些。
(6). Lazy Load Images
這條策略實(shí)際上并不一定能減少HTTP請(qǐng)求數(shù),但是卻能在某些條件下或者頁(yè)面剛加載時(shí)減少HTTP請(qǐng)求數(shù)。對(duì)于圖片而言,在頁(yè)面剛加載的時(shí)候可以只加載第一屏,當(dāng)用戶繼續(xù)往后滾屏的時(shí)候才加載后續(xù)的圖片。這樣一來(lái),假如用戶只對(duì)第一屏的內(nèi)容感興趣時(shí),那剩余的圖片請(qǐng)求就都節(jié)省了。有啊首頁(yè)曾經(jīng)的做法是在加載的時(shí)候把第一屏之后的圖片地址緩存在Textarea標(biāo)簽中,待用戶往下滾屏的時(shí)候才“惰性”加載。
2. 將外部腳本置底
前文有談到,瀏覽器是可以并發(fā)請(qǐng)求的,這一特點(diǎn)使得其能夠更快的加載資源,然而外鏈腳本在加載時(shí)卻會(huì)阻塞其他資源,例如在腳本加載完成之前,它后面的圖片、樣式以及其他腳本都處于阻塞狀態(tài),直到腳本加載完成后才會(huì)開(kāi)始加載。如果將腳本放在比較靠前的位置,則會(huì)影響整個(gè)頁(yè)面的加載速度從而影響用戶體驗(yàn)。解決這一問(wèn)題的方法有很多,在這里有比較詳細(xì)的介紹(這里是譯文和更詳細(xì)的例子),而最簡(jiǎn)單可依賴的方法就是將腳本盡可能的往后挪,減少對(duì)并發(fā)下載的影響。
3. 異步執(zhí)行inline腳本
inline腳本對(duì)性能的影響與外部腳本相比,是有過(guò)之而無(wú)不及。首頁(yè),與外部腳本一樣,inline腳本在執(zhí)行的時(shí)候一樣會(huì)阻塞并發(fā)請(qǐng)求,除此之外,由于瀏覽器在頁(yè)面處理方面是單線程的,當(dāng)inline腳本在頁(yè)面渲染之前執(zhí)行時(shí),頁(yè)面的渲染工作則會(huì)被推遲。簡(jiǎn)而言之,inline腳本在執(zhí)行的時(shí)候,頁(yè)面處于空白狀態(tài)。鑒于以上兩點(diǎn)原因,建議將執(zhí)行時(shí)間較長(zhǎng)的inline腳本異步執(zhí)行,異步的方式有很多種,例如使用script元素的defer屬性(存在兼容性問(wèn)題和其他一些問(wèn)題,例如不能使用document.write)、使用setTimeout,此外,在HTML5中引入了Web Workers的機(jī)制,恰恰可以解決此類(lèi)問(wèn)題。
4. Lazy Load Javascript
隨著Javascript框架的流行,越來(lái)越多的站點(diǎn)也使用起了框架。不過(guò),一個(gè)框架往往包括了很多的功能實(shí)現(xiàn),這些功能并不是每一個(gè)頁(yè)面都需要的,如果下載了不需要的腳本則算得上是一種資源浪費(fèi)-既浪費(fèi)了帶寬又浪費(fèi)了執(zhí)行花費(fèi)的時(shí)間。目前的做法大概有兩種,一種是為那些流量特別大的頁(yè)面專門(mén)定制一個(gè)專用的mini版框架,另一種則是Lazy Load。YUI則使用了第二種方式,在YUI的實(shí)現(xiàn)中,最初只加載核心模塊,其他模塊可以等到需要使用的時(shí)候才加載。
5. 將CSS放在HEAD中
如果將CSS放在其他地方比如BODY中,則瀏覽器有可能還未下載和解析到CSS就已經(jīng)開(kāi)始渲染頁(yè)面了,這就導(dǎo)致頁(yè)面由無(wú)CSS狀態(tài)跳轉(zhuǎn)到CSS狀態(tài),用戶體驗(yàn)比較糟糕。除此之外,有些瀏覽器會(huì)在CSS下載完成后才開(kāi)始渲染頁(yè)面,如果CSS放在靠下的位置則會(huì)導(dǎo)致瀏覽器將渲染時(shí)間推遲。
6. 異步請(qǐng)求Callback
在某些頁(yè)面中可能存在這樣一種需求,需要使用script標(biāo)簽來(lái)異步的請(qǐng)求數(shù)據(jù)。類(lèi)似:
Javascript:
/*Callback函數(shù)*/
function myCallback(info){
//do something here
}
HTML:
<script type="text/javascript" src="http://abc.com/cb"></script>
cb返回的內(nèi)容:
myCallback('Hello world!');
像以上這種方式直接在頁(yè)面上寫(xiě)<script>對(duì)頁(yè)面的性能也是有影響的,即增加了頁(yè)面首次加載的負(fù)擔(dān),推遲了DOMLoaded和window.onload事件的觸發(fā)時(shí)機(jī)。如果時(shí)效性允許的話,可以考慮在DOMLoaded事件觸發(fā)的時(shí)候加載,或者使用setTimeout方式來(lái)靈活的控制加載的時(shí)機(jī)。
7. 減少不必要的HTTP跳轉(zhuǎn)
對(duì)于以目錄形式訪問(wèn)的HTTP鏈接,很多人都會(huì)忽略鏈接最后是否帶’/',假如你的服務(wù)器對(duì)此是區(qū)別對(duì)待的話,那么你也需要注意,這其中很可能隱藏了301跳轉(zhuǎn),增加了多余請(qǐng)求。具體參見(jiàn)下圖,其中第一個(gè)鏈接是以無(wú)’/'結(jié)尾的方式訪問(wèn)的,于是服務(wù)器有了一次跳轉(zhuǎn)。
8. 避免重復(fù)的資源請(qǐng)求
這種情況主要是由于疏忽或頁(yè)面由多個(gè)模塊拼接而成,然后每個(gè)模塊中請(qǐng)求了同樣的資源時(shí),會(huì)導(dǎo)致資源的重復(fù)請(qǐng)求。出現(xiàn)的幾率不大,但是還是要注意排查,不然可能會(huì)出現(xiàn)如下局面,來(lái)自這里。
二、代碼級(jí)優(yōu)化1. Javascript
(1). DOM
DOM操作應(yīng)該是腳本中最耗性能的一類(lèi)操作,例如增加、修改、刪除DOM元素或者對(duì)DOM集合進(jìn)行操作。如果腳本中包含了大量的DOM操作則需要注意以下幾點(diǎn):
a. HTML Collection
在腳本中document.images、document.forms、getElementsByTagName()返回的都是HTMLCollection類(lèi)型的集合,在平時(shí)使用的時(shí)候大多將它作為數(shù)組來(lái)使用,因?yàn)樗衛(wèi)ength屬性,也可以使用索引訪問(wèn)每一個(gè)元素。不過(guò)在訪問(wèn)性能上則比數(shù)組要差很多,原因是這個(gè)集合并不是一個(gè)靜態(tài)的結(jié)果,它表示的僅僅是一個(gè)特定的查詢,每次訪問(wèn)該集合時(shí)都會(huì)重新執(zhí)行這個(gè)查詢從而更新查詢結(jié)果。所謂的“訪問(wèn)集合”包括讀取集合的length屬性、訪問(wèn)集合中的元素。
因此,當(dāng)你需要遍歷HTML Collection的時(shí)候,盡量將它轉(zhuǎn)為數(shù)組后再訪問(wèn),以提高性能。即使不轉(zhuǎn)換為數(shù)組,也請(qǐng)盡可能少的訪問(wèn)它,例如在遍歷的時(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代碼塊中訪問(wèn)非局部變量是都是先從obj上開(kāi)始查找,如果沒(méi)有再依次按作用域鏈向上查找,因此使用with相當(dāng)于增加了作用域鏈長(zhǎng)度。而每次查找作用域鏈都是要消耗時(shí)間的,過(guò)長(zhǎng)的作用域鏈會(huì)導(dǎo)致查找性能下降。
因此,除非你能肯定在with代碼中只訪問(wèn)obj中的屬性,否則慎用with,替代的可以使用局部變量緩存需要訪問(wèn)的屬性。
(3). 避免使用eval和Function
每次 eval 或 Function 構(gòu)造函數(shù)作用于字符串表示的源代碼時(shí),腳本引擎都需要將源代碼轉(zhuǎn)換成可執(zhí)行代碼。這是很消耗資源的操作 —— 通常比簡(jiǎn)單的函數(shù)調(diào)用慢100倍以上。
eval 函數(shù)效率特別低,由于事先無(wú)法知曉傳給 eval 的字符串中的內(nèi)容,eval在其上下文中解釋要處理的代碼,也就是說(shuō)編譯器無(wú)法優(yōu)化上下文,因此只能有瀏覽器在運(yùn)行時(shí)解釋代碼。這對(duì)性能影響很大。
Function 構(gòu)造函數(shù)比eval略好,因?yàn)槭褂么舜a不會(huì)影響周?chē)a;但其速度仍很慢。
此外,使用eval和Function也不利于Javascript壓縮工具執(zhí)行壓縮。
(4). 減少作用域鏈查找
前文談到了作用域鏈查找問(wèn)題,這一點(diǎn)在循環(huán)中是尤其需要注意的問(wèn)題。如果在循環(huán)中需要訪問(wèn)非本作用域下的變量時(shí)請(qǐng)?jiān)诒闅v之前用局部變量緩存該變量,并在遍歷結(jié)束后再重寫(xiě)那個(gè)變量,這一點(diǎn)對(duì)全局變量尤其重要,因?yàn)槿肿兞刻幱谧饔糜蜴湹淖铐敹?,訪問(wèn)時(shí)的查找次數(shù)是最多的。
低效率的寫(xiě)法:
//全局變量
var globalVar = 1;
function myCallback(info){
for( var i = 100000; i--;){
//每次訪問(wèn)globalVar都需要查找到作用域鏈最頂端,本例中需要訪問(wèn)100000次
globalVar += i;
}
}
更高效的寫(xiě)法:
//全局變量
var globalVar = 1;
function myCallback(info){
//局部變量緩存全局變量
var localVar = globalVar;
for( var i = 100000; i--;){
//訪問(wèn)局部變量是最快的
localVar += i;
}
//本例中只需要訪問(wèn)2次全局變量
globalVar = localVar;
}
此外,要減少作用域鏈查找還應(yīng)該減少閉包的使用。
(5). 數(shù)據(jù)訪問(wèn)
Javascript中的數(shù)據(jù)訪問(wèn)包括直接量(字符串、正則表達(dá)式)、變量、對(duì)象屬性以及數(shù)組,其中對(duì)直接量和局部變量的訪問(wèn)是最快的,對(duì)對(duì)象屬性以及數(shù)組的訪問(wèn)需要更大的開(kāi)銷(xiāo)。當(dāng)出現(xiàn)以下情況時(shí),建議將數(shù)據(jù)放入局部變量:
a. 對(duì)任何對(duì)象屬性的訪問(wèn)超過(guò)1次
b. 對(duì)任何數(shù)組成員的訪問(wèn)次數(shù)超過(guò)1次
另外,還應(yīng)當(dāng)盡可能的減少對(duì)對(duì)象以及數(shù)組深度查找。
(6). 字符串拼接
在Javascript中使用"+"號(hào)來(lái)拼接字符串效率是比較低的,因?yàn)槊看芜\(yùn)行都會(huì)開(kāi)辟新的內(nèi)存并生成新的字符串變量,然后將拼接結(jié)果賦值給新變量。與之相比更為高效的做法是使用數(shù)組的join方法,即將需要拼接的字符串放在數(shù)組中最后調(diào)用其join方法得到結(jié)果。不過(guò)由于使用數(shù)組也有一定的開(kāi)銷(xiāo),因此當(dāng)需要拼接的字符串較多的時(shí)候可以考慮用此方法。
關(guān)于Javascript優(yōu)化的更詳細(xì)介紹請(qǐng)參考:
Write Efficient Javascript(PPT)
Efficient JavaScript
2. CSS選擇符
在大多數(shù)人的觀念中,都覺(jué)得瀏覽器對(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),在寫(xiě)選擇符的時(shí)候需要注意很多事項(xiàng),有人已經(jīng)一一列舉了,詳情參考此處。
3. HTML
對(duì)HTML本身的優(yōu)化現(xiàn)如今也越來(lái)越多的受人關(guān)注了,詳情可以參見(jiàn)這篇總結(jié)性文章。
4. Image壓縮
圖片壓縮是個(gè)技術(shù)活,不過(guò)現(xiàn)如今這方面的工具也非常多,壓縮之后往往能帶來(lái)不錯(cuò)的效果,具體的壓縮原理以及方法在《Even Faster Web Sites》第10章有很詳細(xì)的介紹,有興趣的可以去看看。
總結(jié)本文從頁(yè)面級(jí)以及代碼級(jí)兩個(gè)粒度對(duì)前端優(yōu)化的各種方式做了一個(gè)總結(jié),這些方法基本上都是前端開(kāi)發(fā)人員在開(kāi)發(fā)的過(guò)程中可以借鑒和實(shí)踐的,除此之外,完整的前端優(yōu)化還應(yīng)該包括很多其他的途徑,例如CDN、Gzip、多域名、無(wú)Cookie服務(wù)器等等,由于對(duì)于開(kāi)發(fā)人員的可操作性并不強(qiáng)大,在此也就不多敘述了,詳細(xì)的可以參考Yahoo和Google的這些“金科玉律”。
網(wǎng)站標(biāo)題:關(guān)于前端優(yōu)化
分享鏈接:http://www.rwnh.cn/news47/326697.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、手機(jī)網(wǎng)站建設(shè)、商城網(wǎng)站、品牌網(wǎng)站制作、用戶體驗(yàn)、電子商務(wù)
廣告
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源:
創(chuàng)新互聯(lián)