網(wǎng)站的劃分一般為二:前端和后臺癣蟋。我們可以理解成后臺是用來實現(xiàn)網(wǎng)站的功能的幔欧,比如:實現(xiàn)用戶注冊,用戶能夠為文章發(fā)表評論等等。而前端呢?其實應該是屬于功能的表現(xiàn)。并且影響用戶訪問體驗的絕大部分來自前端頁面。
而我們建設網(wǎng)站的目的是什么呢?不就是為了讓目標人群來訪問嗎富腊?所以我們可以理解成前端才是真正和用戶接觸的是整。除了后臺需要在性能上做優(yōu)化外事秀,其實前端的頁面更需要在性能優(yōu)化上下功夫,只有這樣才能給我們的用戶帶來更好的用戶體驗供炼。就好像楚堤,好多人問衅胀,男人在找女朋友的時候是不是只看外表,一些智慧的男人給出了這樣的回答:臉蛋和身材決定了我是否想去了解她的思想,思想決定了我是否會一票否決她的臉蛋和身材丧凤。同理仍侥,網(wǎng)站也是這樣腿时,網(wǎng)站前端的用戶體驗決定了用戶是否想要去使用網(wǎng)站的功能否淤,而網(wǎng)站的功能決定了用戶是否會一票否決前端體驗。
不僅僅如此,如果前端優(yōu)化得好隐解,他不僅可以為企業(yè)節(jié)約成本鞍帝,他還能給用戶帶來更多的用戶,因為增強的用戶體驗煞茫。說了這么多帕涌,那么我們應該如何對我們前端的頁面進行性能優(yōu)化呢?
一般說來续徽,web前端指網(wǎng)站業(yè)務邏輯之前的部分宵膨,包括瀏覽器加載、網(wǎng)站視圖模型炸宵、圖片服務、CDN服務等谷扣,主要優(yōu)化手段有瀏覽器訪問土全、使用反向代理才、CDN等会涎。
瀏覽器訪問優(yōu)化
瀏覽器請求處理流程如下圖:
1裹匙、減少http請求,合理設置 HTTP緩存
http協(xié)議是無狀態(tài)的應用層協(xié)議末秃,意味著每次http請求都需要建立通信鏈路概页、進行數(shù)據(jù)傳輸,而在服務器端练慕,每個http都需要啟動獨立的線程去處理惰匙。這些通信和服務的開銷都很昂貴技掏,減少http請求的數(shù)目可有效提高訪問性能。
減少http的主要手段是合并CSS项鬼、合并JavaScript哑梳、合并圖片。將瀏覽器一次訪問需要的javascript和CSS合并成一個文件绘盟,這樣瀏覽器就只需要一次請求鸠真。圖片也可以合并,多張圖片合并成一張龄毡,如果每張圖片都有不同的超鏈接吠卷,可通過CSS偏移響應鼠標點擊操作,構(gòu)造不同的URL沦零。 緩存的力量是強大的祭隔,恰當?shù)木彺嬖O置可以大大的減少 HTTP請求。假設某網(wǎng)站首頁蠢终,當瀏覽器沒有緩存的時候訪問一共會發(fā)出 78個請求序攘,共 600多 K數(shù)據(jù),而當?shù)诙卧L問即瀏覽器已緩存之后訪問則僅有 10個請求寻拂,共 20多 K數(shù)據(jù)程奠。 (這里需要說明的是,如果直接 F5刷新頁面的話效果是不一樣的祭钉,這種情況下請求數(shù)還是一樣瞄沙,不過被緩存資源的請求服務器是 304響應,只有 Header沒有Body慌核,可以節(jié)省帶寬 )
怎樣才算合理設置 ?原則很簡單距境,能緩存越多越好,能緩存越久越好垮卓。例如垫桂,很少變化的圖片資源可以直接通過 HTTP Header中的Expires設置一個很長的過期頭 ;變化不頻繁而又可能會變的資源可以使用 Last-Modifed來做請求驗證。盡可能的讓資源能夠在緩存中待得更久粟按。關(guān)于 HTTP緩存的具體設置和原理此處就不再詳述了诬滩。
2、使用瀏覽器緩存
對一個網(wǎng)站而言灭将,CSS疼鸟、javascript、logo庙曙、圖標這些靜態(tài)資源文件更新的頻率都比較低空镜,而這些文件又幾乎是每次http請求都需要的,如果將這些文件緩存在瀏覽器中,可以極好的改善性能吴攒。通過設置http頭中的cache-control和expires的屬性张抄,可設定瀏覽器緩存,緩存時間可以是數(shù)天舶斧,甚至是幾個月欣鳖。
在某些時候,靜態(tài)資源文件變化需要及時應用到客戶端瀏覽器茴厉,這種情況泽台,可通過改變文件名實現(xiàn),即更新javascript文件并不是更新javascript文件內(nèi)容矾缓,而是生成一個新的js文件并更新HTML文件中的引用怀酷。 使用瀏覽器緩存策略的網(wǎng)站在更新靜態(tài)資源時,應采用逐量更新的方法嗜闻,比如需要更新10個圖標文件蜕依,不宜把10個文件一次全部更新,而是應該一個文件一個文件逐步更新琉雳,并有一定的間隔時間样眠,以免用戶瀏覽器忽然大量緩存失效,集中更新緩存翠肘,造成服務器負載驟增檐束、網(wǎng)絡堵塞的情況。
3束倍、啟用壓縮
在服務器端對文件進行壓縮被丧,在瀏覽器端對文件解壓縮,可有效減少通信傳輸?shù)臄?shù)據(jù)量绪妹。如果可以的話甥桂,盡可能的將外部的腳本、樣式進行合并邮旷,多個合為一個黄选。文本文件的壓縮效率可達到80%以上,因此HTML婶肩、CSS办陷、javascript文件啟用GZip壓縮可達到較好的效果。但是壓縮對服務器和瀏覽器產(chǎn)生一定的壓力狡孔,在通信帶寬良好,而服務器資源不足的情況下要權(quán)衡考慮蜂嗽。
4苗膝、CSS Sprites
合并 CSS圖片,減少請求數(shù)的又一個好辦法植旧。
5辱揭、LazyLoad Images
這條策略實際上并不一定能減少 HTTP請求數(shù)离唐,但是卻能在某些條件下或者頁面剛加載時減少 HTTP請求數(shù)。對于圖片而言问窃,在頁面剛加載的時候可以只加載第一屏亥鬓,當用戶繼續(xù)往后滾屏的時候才加載后續(xù)的圖片。這樣一來域庇,假如用戶只對第一屏的內(nèi)容感興趣時嵌戈,那剩余的圖片請求就都節(jié)省了。
6听皿、CSS放在頁面最上部熟呛,javascript放在頁面最下面
瀏覽器會在下載完成全部CSS之后才對整個頁面進行渲染,因此最好的做法是將CSS放在頁面最上面尉姨,讓瀏覽器盡快下載CSS庵朝。如果將 CSS放在其他地方比如 BODY中,則瀏覽器有可能還未下載和解析到 CSS就已經(jīng)開始渲染頁面了又厉,這就導致頁面由無 CSS狀態(tài)跳轉(zhuǎn)到 CSS狀態(tài)九府,用戶體驗比較糟糕,所以可以考慮將CSS放在HEAD中覆致。
Javascript則相反侄旬,瀏覽器在加載javascript后立即執(zhí)行,有可能會阻塞整個頁面篷朵,造成頁面顯示緩慢勾怒,因此javascript最好放在頁面最下面。但如果頁面解析時就需要用到javascript声旺,這時放到底部就不合適了笔链。
Lazy Load Javascript(只有在需要加載的時候加載,在一般情況下并不加載信息內(nèi)容腮猖。)隨著 Javascript框架的流行鉴扫,越來越多的站點也使用起了框架。不過澈缺,一個框架往往包括了很多的功能實現(xiàn)坪创,這些功能并不是每一個頁面都需要的,如果下載了不需要的腳本則算得上是一種資源浪費 -既浪費了帶寬又浪費了執(zhí)行花費的時間姐赡。目前的做法大概有兩種莱预,一種是為那些流量特別大的頁面專門定制一個專用的 mini版框架,另一種則是 Lazy Load项滑。
7依沮、異步請求Callback(就是將一些行為樣式提取出來,慢慢的加載信息的內(nèi)容)
在某些頁面中可能存在這樣一種需求,需要使用 script標簽來異步的請求數(shù)據(jù)危喉。類似:
<span style="font-size:14px;">/*Callback 函數(shù)*/
function myCallback(info){
//do something here
}
HTML:
Callback返回的內(nèi)容 :
myCallback('Hello world!');
</span>
像以上這種方式直接在頁面上寫<script>
對頁面的性能也是有影響的宋渔,即增加了頁面首次加載的負擔,推遲了 DOMLoaded和window.onload 事件的觸發(fā)時機辜限。如果時效性允許的話皇拣,可以考慮在 DOMLoaded事件觸發(fā)的時候加載,或者使用 setTimeout方式來靈活的控制加載的時機薄嫡。
8氧急、減少cookie傳輸
一方面,cookie包含在每次請求和響應中岂座,太大的cookie會嚴重影響數(shù)據(jù)傳輸态蒂,因此哪些數(shù)據(jù)需要寫入cookie需要慎重考慮,盡量減少cookie中傳輸?shù)臄?shù)據(jù)量费什。另一方面钾恢,對于某些靜態(tài)資源的訪問,如CSS鸳址、script等瘩蚪,發(fā)送cookie沒有意義,可以考慮靜態(tài)資源使用獨立域名訪問稿黍,避免請求靜態(tài)資源時發(fā)送cookie疹瘦,減少cookie傳輸次數(shù)。
9巡球、Javascript代碼優(yōu)化
(1). DOM
a. HTML Collection(HTML收集器言沐,返回的是一個數(shù)組內(nèi)容信息) 在腳本中 document.images、document.forms酣栈、getElementsByTagName()返回的都是HTMLCollection類型的集合险胰,在平時使用的時候大多將它作為數(shù)組來使用,因為它有 length屬性矿筝,也可以使用索引訪問每一個元素起便。不過在訪問性能上則比數(shù)組要差很多,原因是這個集合并不是一個靜態(tài)的結(jié)果窖维,它表示的僅僅是一個特定的查詢榆综,每次訪問該集合時都會重新執(zhí)行這個查詢從而更新查詢結(jié)果。所謂的“訪問集合” 包括讀取集合的 length屬性铸史、訪問集合中的元素鼻疮。 因此,當你需要遍歷 HTML Collection的時候琳轿,盡量將它轉(zhuǎn)為數(shù)組后再訪問判沟,以提高性能震贵。即使不轉(zhuǎn)換為數(shù)組,也請盡可能少的訪問它水评,例如在遍歷的時候可以將 length屬性、成員保存到局部變量后再使用局部變量媚送≈性铮
b. Reflow & Repaint 除了上面一點之外, DOM操作還需要考慮瀏覽器的Reflow和Repaint 塘偎,因為這些都是需要消耗資源的疗涉。
(2). 慎用 with
with(obj){ p = 1}; 代碼塊的行為實際上是修改了代碼塊中的執(zhí)行環(huán)境 ,將obj放在了其作用域鏈的最前端吟秩,在 with代碼塊中訪問非局部變量是都是先從 obj上開始查找咱扣,如果沒有再依次按作用域鏈向上查找,因此使用 with相當于增加了作用域鏈長度涵防。而每次查找作用域鏈都是要消耗時間的闹伪,過長的作用域鏈會導致查找性能下降。 因此壮池,除非你能肯定在 with代碼中只訪問 obj中的屬性偏瓤,否則慎用 with,替代的可以使用局部變量緩存需要訪問的屬性椰憋。
(3). 避免使用 eval和 Function
每次 eval 或Function 構(gòu)造函數(shù)作用于字符串表示的源代碼時厅克,腳本引擎都需要將源代碼轉(zhuǎn)換成可執(zhí)行代碼。這是很消耗資源的操作 —— 通常比簡單的函數(shù)調(diào)用慢 100倍以上橙依。 eval 函數(shù)效率特別低证舟,由于事先無法知曉傳給 eval 的字符串中的內(nèi)容,eval在其上下文中解釋要處理的代碼窗骑,也就是說編譯器無法優(yōu)化上下文女责,因此只能有瀏覽器在運行時解釋代碼。這對性能影響很大慧域。 Function 構(gòu)造函數(shù)比 eval略好鲤竹,因為使用此代碼不會影響周圍代碼 ;但其速度仍很慢。 此外昔榴,使用 eval和 Function也不利于Javascript 壓縮工具執(zhí)行壓縮辛藻。
(4). 減少作用域鏈查找
前文談到了作用域鏈查找問題,這一點在循環(huán)中是尤其需要注意的問題互订。如果在循環(huán)中需要訪問非本作用域下的變量時請在遍歷之前用局部變量緩存該變量吱肌,并在遍歷結(jié)束后再重寫那個變量,這一點對全局變量尤其重要仰禽,因為全局變量處于作用域鏈的最頂端氮墨,訪問時的查找次數(shù)是最多的纺蛆。
低效率的寫法:
<span style="font-size:14px;">// 全局變量
var globalVar = 1;
function myCallback(info){
for( var i = 100000; i--;){
//每次訪問 globalVar 都需要查找到作用域鏈最頂端,本例中需要訪問 100000 次
globalVar += i;
}
}
</span>
更高效的寫法:
<span style="font-size:14px;">// 全局變量
var globalVar = 1;
function myCallback(info){
//局部變量緩存全局變量
var localVar = globalVar;
for( var i = 100000; i--;){
//訪問局部變量是最快的
localVar += i;
}
//本例中只需要訪問 2次全局變量
在函數(shù)中只需要將 globalVar中內(nèi)容的值賦給localVar 中
globalVar = localVar;
}
</span>
此外规揪,要減少作用域鏈查找還應該減少閉包的使用桥氏。
(5). 數(shù)據(jù)訪問
Javascript中的數(shù)據(jù)訪問包括直接量 (字符串、正則表達式 )、變量、對象屬性以及數(shù)組鸦概,其中對直接量和局部變量的訪問是最快的,對對象屬性以及數(shù)組的訪問需要更大的開銷堕伪。當出現(xiàn)以下情況時,建議將數(shù)據(jù)放入局部變量:
a. 對任何對象屬性的訪問超過 1次
b. 對任何數(shù)組成員的訪問次數(shù)超過 1次 .另外栗菜,還應當盡可能的減少對對象以及數(shù)組深度查找欠雌。
(6). 字符串拼接
在 Javascript中使用”+”號來拼接字符串效率是比較低的,因為每次運行都會開辟新的內(nèi)存并生成新的字符串變量疙筹,然后將拼接結(jié)果賦值給新變量富俄。與之相比更為高效的做法是使用數(shù)組的 join方法,即將需要拼接的字符串放在數(shù)組中最后調(diào)用其 join方法得到結(jié)果而咆。不過由于使用數(shù)組也有一定的開銷蛙酪,因此當需要拼接的字符串較多的時候可以考慮用此方法。
10翘盖、CSS選擇符優(yōu)化
在大多數(shù)人的觀念中桂塞,都覺得瀏覽器對 CSS選擇符的解析式從左往右進行的,例如 #toc A { color: #444; }
這樣一個選擇符馍驯,如果是從右往左解析則效率會很高阁危,因為第一個 ID選擇基本上就把查找的范圍限定了,但實際上瀏覽器對選擇符的解析是從右往左進行的汰瘫。如上面的選擇符狂打,瀏覽器必須遍歷查找每一個 A標簽的祖先節(jié)點,效率并不像之前想象的那樣高混弥。根據(jù)瀏覽器的這一行為特點趴乡,在寫選擇符的時候需要注意很多事項,有興趣的童鞋可以去了解一下蝗拿。
CDN加速
CDN(contentdistribute network晾捏,內(nèi)容分發(fā)網(wǎng)絡)的本質(zhì)仍然是一個緩存,而且將數(shù)據(jù)緩存在離用戶最近的地方哀托,使用戶以最快速度獲取數(shù)據(jù)惦辛,即所謂網(wǎng)絡訪問第一跳,如下圖仓手。
由于CDN部署在網(wǎng)絡運營商的機房胖齐,這些運營商又是終端用戶的網(wǎng)絡服務提供商玻淑,因此用戶請求路由的第一跳就到達了CDN服務器,當CDN中存在瀏覽器請求的資源時呀伙,從CDN直接返回給瀏覽器补履,最短路徑返回響應,加快用戶訪問速度剿另,減少數(shù)據(jù)中心負載壓力干像。 CDN緩存的一般是靜態(tài)資源,如圖片驰弄、文件、CSS速客、script腳本戚篙、靜態(tài)網(wǎng)頁等,但是這些文件訪問頻度很高溺职,將其緩存在CDN可極大改善網(wǎng)頁的打開速度岔擂。
反向代理
傳統(tǒng)代理服務器位于瀏覽器一側(cè),代理瀏覽器將http請求發(fā)送到互聯(lián)網(wǎng)上浪耘,而反向代理服務器位于網(wǎng)站機房一側(cè)乱灵,代理網(wǎng)站web服務器接收http請求。如下圖所示:
論壇網(wǎng)站七冲,把熱門詞條痛倚、帖子、博客緩存在反向代理服務器上加速用戶訪問速度澜躺,當這些動態(tài)內(nèi)容有變化時蝉稳,通過內(nèi)部通知機制通知反向代理緩存失效,反向代理會重新加載最新的動態(tài)內(nèi)容再次緩存起來掘鄙。
此外耘戚,反向代理也可以實現(xiàn)負載均衡的功能,而通過負載均衡構(gòu)建的應用集群可以提高系統(tǒng)總體處理能力操漠,進而改善網(wǎng)站高并發(fā)情況下的性能收津。