一、瀏覽器訪問優(yōu)化
1涮毫、減少HTTP請求
http協(xié)議是無狀態(tài)的應(yīng)用層協(xié)議翘魄,意味著每次http請求都需要建立通信鏈路、進行數(shù)據(jù)傳輸,而在服務(wù)器端谋币,每個http都需要啟動獨立的線程去處理仗扬。這些通信和服務(wù)的開銷都很昂貴,減少http請求的數(shù)目可有效提高訪問性能蕾额。
1)合并CSS建炫、合并javascript听怕、合并圖片。將瀏覽器一次訪問需要的javascript和CSS合并成一個文件,這樣瀏覽器就只需要一次請求赠法。圖片也可以合并,多張圖片合并成一張陶舞,如果每張圖片都有不同的超鏈接捡多,可通過CSS偏移響應(yīng)鼠標(biāo)點擊操作,構(gòu)造不同的URL筐眷。
2)合理設(shè)置緩存黎烈。很少變化的圖片資源可以直接通過 HTTP Header中的Expires設(shè)置一個很長的過期頭 ;變化不頻繁而又可能會變的資源可以使用 Last-Modifed來做請求驗證。盡可能的讓資源能夠在緩存中待得更久匀谣。
2照棋、使用瀏覽器緩存
1) 將更新頻率比較低的CSS、javascript武翎、logo烈炭、圖標(biāo)等靜態(tài)資源文件緩存在瀏覽器中,避免頻繁的http請求宝恶。通過設(shè)置http頭中的cache-control和expires的屬性符隙,可設(shè)定瀏覽器緩存,緩存時間可以是數(shù)天垫毙,甚至是幾個月霹疫。
2)當(dāng)靜態(tài)資源文件發(fā)生變化時,通過生成一個新的JS文件并更新HTML文件中的引用來更新js文件综芥,避免直接更新js文件中的內(nèi)容丽蝎。
3)使用瀏覽器緩存策略的網(wǎng)站在更新靜態(tài)資源時,應(yīng)采用逐量更新的方法膀藐,比如需要更新10個圖標(biāo)文件屠阻,不宜把10個文件一次全部更新,而是應(yīng)該一個文件一個文件逐步更新额各,并有一定的間隔時間国觉,避免用戶瀏覽器忽然大量緩存失效,集中更新緩存虾啦,造成服務(wù)器負載驟增蛉加、網(wǎng)絡(luò)堵塞的情況蚜枢。
3、啟用壓縮
在服務(wù)器端對文件進行壓縮针饥,在瀏覽器端對文件解壓縮可有效減少通信傳輸?shù)臄?shù)據(jù)量厂抽。如果可以的話,盡可能的將外部的腳本丁眼、樣式進行合并筷凤,多個合為一個。文本文件的壓縮效率可達到80%以上苞七,因此HTML藐守、CSS、javascript文件啟用GZip壓縮可達到較好的效果蹂风。但是壓縮對服務(wù)器和瀏覽器產(chǎn)生一定的壓力卢厂,在通信帶寬良好,而服務(wù)器資源不足的情況下要權(quán)衡考慮惠啄。
4慎恒、CSS Sprites
合并CSS圖片,減少HTTP請求數(shù)撵渡。
5融柬、LazyLoad Images
這條策略實際上并不一定能減少 HTTP請求數(shù),但是卻能在某些條件下或者頁面剛加載時減少 HTTP請求數(shù)趋距。對于圖片而言粒氧,在頁面剛加載的時候可以只加載第一屏,當(dāng)用戶繼續(xù)往后滾屏的時候才加載后續(xù)的圖片节腐。這樣一來外盯,假如用戶只對第一屏的內(nèi)容感興趣時,那剩余的圖片請求就都節(jié)省了翼雀。
6饱苟、CSS放在頁面最上部,javascript放在頁面最下面
瀏覽器會在下載完成全部CSS之后才對整個頁面進行渲染锅纺,因此最好的做法是將CSS放在頁面最上面,讓瀏覽器盡快下載CSS肋殴。如果將 CSS放在其他地方比如 BODY中囤锉,則瀏覽器有可能還未下載和解析到 CSS就已經(jīng)開始渲染頁面了,這就導(dǎo)致頁面由無 CSS狀態(tài)跳轉(zhuǎn)到 CSS狀態(tài)护锤,用戶體驗比較糟糕官地,所以可以考慮將CSS放在HEAD中。
Javascript則相反烙懦,瀏覽器在加載javascript后立即執(zhí)行驱入,有可能會阻塞整個頁面,造成頁面顯示緩慢,因此javascript最好放在頁面最下面亏较。但如果頁面解析時就需要用到j(luò)avascript莺褒,這時放到底部就不合適了。
7雪情、Lazy Load Javascript(只有在需要加載的時候加載遵岩,在一般情況下并不加載信息內(nèi)容。)
隨著 Javascript框架的流行巡通,越來越多的站點也使用起了框架尘执。不過,一個框架往往包括了很多的功能實現(xiàn)宴凉,這些功能并不是每一個頁面都需要的誊锭,如果下載了不需要的腳本則算得上是一種資源浪費
-既浪費了帶寬又浪費了執(zhí)行花費的時間。目前的做法大概有兩種弥锄,一種是為那些流量特別大的頁面專門定制一個專用的 mini版框架丧靡,另一種則是 Lazy Load。
8叉讥、異步請求Callback(就是將一些行為樣式提取出來窘行,慢慢的加載信息的內(nèi)容)
在某些頁面中可能存在這樣一種需求,需要使用 script標(biāo)簽來異步的請求數(shù)據(jù)图仓。類似:
<span style="font-size:14px;">/*Callback 函數(shù)*/
function myCallback(info){
//do something here
}
HTML:
Callback返回的內(nèi)容 :
myCallback('Hello world!');
</span>
像以上這種方式直接在頁面上寫 <script> 對頁面的性能也是有影響的罐盔,即增加了頁面首次加載的負擔(dān),推遲了 DOMLoaded和window.onload 事件的觸發(fā)時機救崔。如果時效性允許的話惶看,可以考慮在 DOMLoaded事件觸發(fā)的時候加載,或者使用 setTimeout方式來靈活的控制加載的時機六孵。
9纬黎、減少cookie傳輸
一方面,cookie包含在每次請求和響應(yīng)中劫窒,太大的cookie會嚴重影響數(shù)據(jù)傳輸本今,因此哪些數(shù)據(jù)需要寫入cookie需要慎重考慮,盡量減少cookie中傳輸?shù)臄?shù)據(jù)量主巍。另一方面冠息,對于某些靜態(tài)資源的訪問,如CSS孕索、script等逛艰,發(fā)送cookie沒有意義,可以考慮靜態(tài)資源使用獨立域名訪問搞旭,避免請求靜態(tài)資源時發(fā)送cookie散怖,減少cookie傳輸次數(shù)菇绵。
10、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屬性、訪問集合中的元素啼肩。
因此橄妆,當(dāng)你需要遍歷 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相當(dāng)于增加了作用域鏈長度民效。而每次查找作用域鏈都是要消耗時間的,過長的作用域鏈會導(dǎo)致查找性能下降涛救。
因此畏邢,除非你能肯定在 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>
此外,要減少作用域鏈查找還應(yīng)該減少閉包的使用捶索。
(5). 數(shù)據(jù)訪問
Javascript中的數(shù)據(jù)訪問包括直接量 (字符串插掂、正則表達式 )、變量情组、對象屬性以及數(shù)組燥筷,其中對直接量和局部變量的訪問是最快的,對對象屬性以及數(shù)組的訪問需要更大的開銷院崇。當(dāng)出現(xiàn)以下情況時肆氓,建議將數(shù)據(jù)放入局部變量:
a. 對任何對象屬性的訪問超過 1次
b. 對任何數(shù)組成員的訪問次數(shù)超過 1次
另外,還應(yīng)當(dāng)盡可能的減少對對象以及數(shù)組深度查找底瓣。
(6). 字符串拼接
在 Javascript中使用”+”號來拼接字符串效率是比較低的谢揪,因為每次運行都會開辟新的內(nèi)存并生成新的字符串變量,然后將拼接結(jié)果賦值給新變量捐凭。與之相比更為高效的做法是使用數(shù)組的 join方法拨扶,即將需要拼接的字符串放在數(shù)組中最后調(diào)用其 join方法得到結(jié)果。不過由于使用數(shù)組也有一定的開銷茁肠,因此當(dāng)需要拼接的字符串較多的時候可以考慮用此方法患民。
10、CSS選擇符優(yōu)化
在大多數(shù)人的觀念中垦梆,都覺得瀏覽器對 CSS選擇符的解析式從左往右進行的匹颤,例如
#toc A { color: #444; }這樣一個選擇符仅孩,如果是從右往左解析則效率會很高,因為第一個 ID選擇基本上就把查找的范圍限定了印蓖,但實際上瀏覽器對選擇符的解析是從右往左進行的辽慕。如上面的選擇符,瀏覽器必須遍歷查找每一個 A標(biāo)簽的祖先節(jié)點赦肃,效率并不像之前想象的那樣高溅蛉。根據(jù)瀏覽器的這一行為特點,在寫選擇符的時候需要注意很多事項他宛。
11船侧、CDN加速
CDN(contentdistribute network,內(nèi)容分發(fā)網(wǎng)絡(luò))的本質(zhì)仍然是一個緩存厅各,而且將數(shù)據(jù)緩存在離用戶最近的地方勺爱,使用戶以最快速度獲取數(shù)據(jù),即所謂網(wǎng)絡(luò)訪問第一跳讯检,如下圖琐鲁。
由于CDN部署在網(wǎng)絡(luò)運營商的機房,這些運營商又是終端用戶的網(wǎng)絡(luò)服務(wù)提供商人灼,因此用戶請求路由的第一跳就到達了CDN服務(wù)器围段,當(dāng)CDN中存在瀏覽器請求的資源時投放,從CDN直接返回給瀏覽器灸芳,最短路徑返回響應(yīng)烙样,加快用戶訪問速度,減少數(shù)據(jù)中心負載壓力蛤肌。
CDN緩存的一般是靜態(tài)資源裸准,如圖片炒俱、文件权悟、CSS僵芹、script腳本小槐、靜態(tài)網(wǎng)頁等,但是這些文件訪問頻度很高件豌,將其緩存在CDN可極大改善網(wǎng)頁的打開速度茧彤。
2疆栏、反向代理
傳統(tǒng)代理服務(wù)器位于瀏覽器一側(cè),代理瀏覽器將http請求發(fā)送到互聯(lián)網(wǎng)上珠洗,而反向代理服務(wù)器位于網(wǎng)站機房一側(cè)许蓖,代理網(wǎng)站web服務(wù)器接收http請求膊爪。如下圖所示:
論壇網(wǎng)站米酬,把熱門詞條淮逻、帖子爬早、博客緩存在反向代理服務(wù)器上加速用戶訪問速度,當(dāng)這些動態(tài)內(nèi)容有變化時饶米,通過內(nèi)部通知機制通知反向代理緩存失效,反向代理會重新加載最新的動態(tài)內(nèi)容再次緩存起來照瘾。
此外匈棘,反向代理也可以實現(xiàn)負載均衡的功能析命,而通過負載均衡構(gòu)建的應(yīng)用集群可以提高系統(tǒng)總體處理能力,進而改善網(wǎng)站高并發(fā)情況下的性能鹃愤。
二簇搅、湃硗拢客網(wǎng)Flappy Bird是風(fēng)靡一時的手機游戲瘩将,玩家要操作一只小鳥穿過無窮無盡的由鋼管組成的障礙。如果要你在HTML前端開發(fā)這個游戲凹耙,為了保證游戲的流暢運行,并長時間運行也不會崩潰肖抱,請列舉開發(fā)要注意的性能問題和解決的方法虐沥。
①背景的卷軸效果優(yōu)化欲险。背景不能是無限長的圖片拼接天试,必須及時釋放掉不在畫面中的DOM元素。
②將復(fù)雜運算從主UI線程中解耦。比如場景中小鳥的運動軌跡带兜、碰撞算法等枫笛,不能和UI動畫同時進行「照眨可以使用webworker開啟單獨的線程處理復(fù)雜運算刑巧。
③注意內(nèi)存泄漏和回收。使用對象池管理內(nèi)存,提高內(nèi)存檢測和垃圾回收啊楚。