|
前言: 在同樣的網(wǎng)絡(luò)環(huán)境下,兩個同樣能滿足你的需求的網(wǎng)站,一個“Duang”的一下就加載出來了,一個糾結(jié)了半天才出來,你會選擇哪個?研究表明:用戶最滿意的打開網(wǎng)頁時間是2-5秒,如果等待超過10秒,99%的用戶會關(guān)閉這個網(wǎng)頁。也許這樣講,各位還不會有太多感觸,接下來我列舉一組數(shù)據(jù):Google網(wǎng)站訪問速度每慢400ms就導(dǎo)致用戶搜索請 求下降0.59%;Amazon每增加100ms網(wǎng)站延遲將導(dǎo)致收入下降1%;雅虎如果有400ms延遲會導(dǎo)致流量下降5-9%。網(wǎng)站的加載速度嚴重影響了用戶體驗,也決定了這個網(wǎng)站的生死存亡。 可能有人會說:網(wǎng)站的性能是后端工程師的事情,與前端并無多大關(guān)系。我只能說,too young too simple。事實上,只有10%~20%的最終用戶響應(yīng)時間是用在從Web服務(wù)器獲取HTML文檔并傳送到瀏覽器的,那剩余的時間去哪兒了?來瞄一下性能黃金法則: 只有10%~20%的最終用戶響應(yīng)時間花在了下載HTML文檔上。其余的80%~90%時間花在了下載頁面中的所有組件上。 接下來我們將研究一下前端攻城獅如何來提高頁面的加載速度。 一、減少HTTP請求上面說到80%~90%時間花在了下載頁面中的所有組件進行的HTTP請求上。因此,改善響應(yīng)時間最簡單的途徑就是減少HTTP請求的數(shù)量。 圖片地圖:假設(shè)導(dǎo)航欄上有五幅圖片,點擊每張圖片都會進入一個鏈接,這樣五張導(dǎo)航的圖片在加載時會產(chǎn)生5個HTTP請求。然而,使用一個圖片地圖可以提高效率,這樣就只需要一個HTTP請求。 客戶端圖片地圖:直接將點擊映射到操作
使用圖片地圖的缺點:指定坐標區(qū)域時,矩形或圓形比較容易指定,而其它形狀手工指定比較難 CSS SpritesCSS Sprites直譯過來就是CSS精靈,但是這種翻譯顯然是不夠的,其實就是通過將多個圖片融合到一副圖里面,然后通過CSS的一些技術(shù)布局到網(wǎng)頁上。特別是圖片特別多的網(wǎng)站,如果能用css sprites降低圖片數(shù)量,帶來的將是速度的提升。
運行結(jié)果: PS:使用CSS Sprites還有可能降低下載量,可能大家會認為合并后的圖片會比分離圖片的總和要大,因為還有可能會附加空白區(qū)域。實際上,合并后的圖片會比分離的圖片總和要小,因為它降低了圖片自身的開銷,譬如顏色表、格式信息等。 字體圖標 在可以大量使用字體圖標的地方我們可以盡可能使用字體圖標,字體圖標可以減少很多圖片的使用,從而減少http請求,字體圖標還可以通過CSS來設(shè)置顏色、大小等樣式,何樂而不為。 合并腳本 和樣式表 將多個樣式表或者腳本文件合并到一個文件中,可以減少HTTP請求的數(shù)量從而縮短效應(yīng)時間。 然而合并所有文件對許多人尤其是編寫模塊化代碼的人來說是不能忍的,而且合并所有的樣式文件或者腳本文件可能會導(dǎo)致在一個頁面加載時加載了多于自己所需要的樣式或者腳本,對于只訪問該網(wǎng)站一個(或幾個)頁面的人來說反而增加了下載量,所以大家應(yīng)該自己權(quán)衡利弊。 二、使用CDN如果應(yīng)用程序web服務(wù)器離用戶更近,那么一個HTTP請求的響應(yīng)時間將縮短。另一方面,如果組件web服務(wù)器離用戶更近,則多個HTTP請求的響應(yīng)時間將縮短。 CDN(內(nèi)容發(fā)布網(wǎng)絡(luò))是一組分布在多個不同地理位置的Web服務(wù)器,用于更加有效地向用戶發(fā)布內(nèi)容。在優(yōu)化性能時,向特定用戶發(fā)布內(nèi)容的服務(wù)器的選擇基于對網(wǎng)絡(luò)慕課擁堵的測量。例如,CDN可能選擇網(wǎng)絡(luò)階躍數(shù)最小的服務(wù)器,或者具有最短響應(yīng)時間的服務(wù)器。 CDN還可以進行數(shù)據(jù)備份、擴展存儲能力,進行緩存,同時有助于緩和Web流量峰值壓力。 CDN的缺點: 1、響應(yīng)時間可能會受到其他網(wǎng)站流量的影響。CDN服務(wù)提供商在其所有客戶之間共享Web服務(wù)器組。 2、如果CDN服務(wù)質(zhì)量下降了,那么你的工作質(zhì)量也將下降 3、無法直接控制組件服務(wù)器 三、添加Expires頭頁面的初次訪問者會進行很多HTTP請求,但是通過使用一個長久的Expires頭,可以使這些組件被緩存,下次訪問的時候,就可以減少不必要的HTPP請求,從而提高加載速度。 Web服務(wù)器通過Expires頭告訴客戶端可以使用一個組件的當前副本,直到指定的時間為止。例如: Expires: Fri, 18 Mar 2016 07:41:53 GMT Expires缺點: 它要求服務(wù)器和客戶端時鐘嚴格同步;過期日期需要經(jīng)常檢查 HTTP1.1中引入Cache-Control來克服Expires頭的限制,使用max-age指定組件被緩存多久。 Cache-Control: max-age=12345600 若同時制定Cache-Control和Expires,則max-age將覆蓋Expires頭. 四、壓縮組件從HTTP1.1開始,Web客戶端可以通過HTTP請求中的Accept-Encoding頭來表示對壓縮的支持 Accept-Encoding: gzip,deflate 如果Web服務(wù)器看到請求中有這個頭,就會使用客戶端列出來的方法中的一種來進行壓縮。Web服務(wù)器通過響應(yīng)中的Content-Encoding來通知 Web客戶端。 Content-Encoding: gzip 代理緩存當瀏覽器通過代理來發(fā)送請求時,情況會不一樣。假設(shè)針對某個URL發(fā)送到代理的第一個請求來自于一個不支持gzip的瀏覽器。這是代理的第一個請求,緩存為空。代理將請求轉(zhuǎn)發(fā)給服務(wù)器。此時響應(yīng)是未壓縮的,代理緩存同時發(fā)送給瀏覽器。現(xiàn)在,假設(shè)到達代理的請求是同一個url,來自于一個支持gzip的瀏覽器。代理會使用緩存中未壓縮的內(nèi)容進行響應(yīng),從而失去了壓縮的機會。相反,如果第一個瀏覽器支持gzip,第二個不支持,你們代理緩存中的壓縮版本將會提供給后續(xù)的瀏覽器,而不管它們是否支持gzip。 解決辦法:在web服務(wù)器的響應(yīng)中添加vary頭Web服務(wù)器可以告訴代理根據(jù)一個或多個請求頭來改變緩存的響應(yīng)。因為壓縮的決定是基于Accept-Encoding請求頭的,因此需要在vary響應(yīng)頭中包含Accept-Encoding。 vary: Accept-Encoding 五、將樣式表放在頭部首先說明一下,將樣式表放在頭部對于實際頁面加載的時間并不能造成太大影響,但是這會減少頁面首屏出現(xiàn)的時間,使頁面內(nèi)容逐步呈現(xiàn),改善用戶體驗,防止“白屏”。 我們總是希望頁面能夠盡快顯示內(nèi)容,為用戶提供可視化的回饋,這對網(wǎng)速慢的用戶來說是很重要的。 將樣式表放在文檔底部會阻止瀏覽器中的內(nèi)容逐步出現(xiàn)。為了避免當樣式變化時重繪頁面元素,瀏覽器會阻塞內(nèi)容逐步呈現(xiàn),造成“白屏”。這源自瀏覽器的行為:如果樣式表仍在加載,構(gòu)建呈現(xiàn)樹就是一種浪費,因為所有樣式表加載解析完畢之前務(wù)虛會之任何東西。 六、將腳本放在底部更樣式表相同,腳本放在底部對于實際頁面加載的時間并不能造成太大影響,但是這會減少頁面首屏出現(xiàn)的時間,使頁面內(nèi)容逐步呈現(xiàn)。 js的下載和執(zhí)行會阻塞Dom樹的構(gòu)建(嚴謹?shù)卣f是中斷了Dom樹的更新),所以script標簽放在首屏范圍內(nèi)的HTML代碼段里會截斷首屏的內(nèi)容。 下載腳本時并行下載是被禁用的——即使使用了不同的主機名,也不會啟用其他的下載。因為腳本可能修改頁面內(nèi)容,因此瀏覽器會等待;另外,也是為了保證腳本能夠按照正確的順序執(zhí)行,因為后面的腳本可能與前面的腳本存在依賴關(guān)系,不按照順序執(zhí)行可能會產(chǎn)生錯誤。 七、避免CSS表達式CSS表達式是動態(tài)設(shè)置CSS屬性的一種強大并且危險的方式,它受到了IE5以及之后版本、IE8之前版本的支持。
如何解決: 一次性表達式:
事件處理機制用js事件處理機制來動態(tài)改變元素的樣式,使函數(shù)運行次數(shù)在可控范圍之內(nèi)。 八、使用外部的JavaScript和CSS內(nèi)聯(lián)腳本或者樣式可以減少HTTP請求,按理來說可以提高頁面加載的速度。然而在實際情況中,當腳本或者樣式是從外部引入的文件,瀏覽器就有可能緩存它們,從而在以后加載的時候能夠直接使用緩存,而HTML文檔的大小減小,從而提高加載速度。 影響因素: 1、每個用戶產(chǎn)生的頁面瀏覽量越少,內(nèi)聯(lián)腳本和樣式的論據(jù)越強勢。譬如一個用戶每個月只訪問你的網(wǎng)站一兩次,那么這種情況下內(nèi)聯(lián)將會更好。而如果該用戶能夠產(chǎn)生很多頁面瀏覽量,那么緩存的樣式和腳本將會極大減少下載的時間,提交頁面加載速度。 2、如果你的網(wǎng)站不同的頁面之間使用的組件大致相同,那么使用外部文件可以提高這些組件的重用率。 加載后下載有時候我們希望內(nèi)聯(lián)樣式和腳本,但又可以為接下來的頁面提供外部文件。那么我們可以在頁面加載完成止嘔動態(tài)加載外部組件,以便用戶接下來的訪問。
在該頁面中,JavaScript和CSS被加載兩次(內(nèi)聯(lián)和外部)。要使其正常工作,必須處理雙重定義。將這些組件放到一個不可見的IFrame中是一個比較好的解決方式。 九、減少DNS查找當我們在瀏覽器的地址欄輸入網(wǎng)址(譬如: www.linux178.com) ,然后回車,回車這一瞬間到看到頁面到底發(fā)生了什么呢? 域名解析 –> 發(fā)起TCP的3次握手 –> 建立TCP連接后發(fā)起http請求 –> 服務(wù)器響應(yīng)http請求,瀏覽器得到html代碼 –> 瀏覽器解析html代碼,并請求html代碼中的資源(如js、css、圖片等) –> 瀏覽器對頁面進行渲染呈現(xiàn)給用戶 域名解析是頁面加載的第一步,那么域名是如何解析的呢?以Chrome為例:
DNS也是開銷,通常瀏覽器查找一個給定域名的IP地址要花費20~120毫秒,在完成域名解析之前,瀏覽器不能從服務(wù)器加載到任何東西。那么如何減少域名解析時間,加快頁面加載速度呢? 當客戶端DNS緩存(瀏覽器和操作系統(tǒng))緩存為空時,DNS查找的數(shù)量與要加載的Web頁面中唯一主機名的數(shù)量相同,包括頁面URL、腳本、樣式表、圖片、Flash對象等的主機名。減少主機名的 數(shù)量就可以減少DNS查找的數(shù)量。 減少唯一主機名的數(shù)量會潛在減少頁面中并行下載的數(shù)量(HTTP 1.1規(guī)范建議從每個主機名并行下載兩個組件,但實際上可以多個),這樣減少主機名和并行下載的方案會產(chǎn)生矛盾,需要大家自己權(quán)衡。建議將組件放到至少兩個但不多于4個主機名下,減少DNS查找的同時也允許高度并行下載。 十、精簡JavaScript精簡 精簡就是從代碼中移除不必要的字符以減少文件大小,降低加載的時間。代碼精簡的時候會移除不必要的空白字符(空格,換行、制表符),這樣整個文件的大小就變小了。 混淆 混淆是應(yīng)用在源代碼上的另外一種方式,它會移除注釋和空白符,同時它還會改寫代碼。在混淆的時候,函數(shù)和變量名將會被轉(zhuǎn)換成更短的字符串,這時代碼會更加精煉同時難以閱讀。通常這樣做是為了增加對代碼進行反向工程的難度,這也同時提高了性能。 缺點: 混淆本身比較復(fù)雜,可能會引入錯誤。 需要對不能改變的符號做標記,防止JavaScript符號(譬如關(guān)鍵字、保留字)被修改。 混淆會使代碼難以閱讀,這使得在產(chǎn)品環(huán)境中調(diào)試問題更加困難。 在以上提到了關(guān)于用gzip之類的壓縮方式來壓縮文件,這邊說明一下,就算使用gzip等方式來壓縮文件,精簡代碼依然是有必要的。一般來說,壓縮產(chǎn)生的節(jié)省是高于精簡的,在生產(chǎn)環(huán)境中,精簡和壓縮同時使用能夠最大限度的獲得更多的節(jié)省。 CSS的精簡CSS的精簡帶來的節(jié)省一般來說是小于JavaScript精簡的,因為CSS中注釋和空白相對較少。 除了移除空白、注釋之外,CSS可以通過優(yōu)化來獲得更多的節(jié)?。?/p> 合并相同的類; 移除不使用的類; 使用縮寫,譬如
上面.right是正確的的寫法,顏色使用縮寫,使用0代替0px,合并可以合并的樣式。另外,在精簡的時候其實樣式最后一行的’;’也是可以省略的。 來看看精簡的例子: 十一、避免重定向什么是重定向?重定向用于將用戶從一個URL重新路由到另一個URL。 常用重定向的類型301:永久重定向,主要用于當網(wǎng)站的域名發(fā)生變更之后,告訴搜索引擎域名已經(jīng)變更了,應(yīng)該把舊域名的的數(shù)據(jù)和鏈接數(shù)轉(zhuǎn)移到新域名下,從而不會讓網(wǎng)站的排名因域名變更而受到影響。 302:臨時重定向,主要實現(xiàn)post請求后告知瀏覽器轉(zhuǎn)移到新的URL。 304:Not Modified,主要用于當瀏覽器在其緩存中保留了組件的一個副本,同時組件已經(jīng)過期了,這是瀏覽器就會生成一個條件GET請求,如果服務(wù)器的組件并沒有修改過,則會返回304狀態(tài)碼,同時不攜帶主體,告知瀏覽器可以重用這個副本,減少響應(yīng)大小。 重定向如何損傷性能?當頁面發(fā)生了重定向,就會延遲整個HTML文檔的傳輸。在HTML文檔到達之前,頁面中不會呈現(xiàn)任何東西,也沒有任何組件會被下載。 來看一個實際例子:對于ASP.NET webform開發(fā)來說,對于新手很容易犯一個錯誤,就是把頁面的連接寫成服務(wù)器控件后臺代碼里,例如用一個Button控件,在它的后臺click事件中寫上:Response.Redirect(“”);然而這個Button的作用只是轉(zhuǎn)移URL,這是非常低效的做法,因為點擊Button后,先發(fā)送一個Post請求給服務(wù)器,服務(wù)器處理Response.Redirect(“”)后就發(fā)送一個302響應(yīng)給瀏覽器,瀏覽器再根據(jù)響應(yīng)的URL發(fā)送GET請求。正確的做法應(yīng)該是在html頁面直接使用a標簽做鏈接,這樣就避免了多余的post和重定向。 重定向的應(yīng)用場景1. 跟蹤內(nèi)部流量重定向經(jīng)常用于跟蹤用戶流量的方向,當擁有一個門戶主頁的時候,同時想對用戶離開主頁后的流量進行跟蹤,這時可以使用重定向。例如: 某網(wǎng)站主頁新聞的鏈接地址http:///r/news,點擊該鏈接將產(chǎn)生301響應(yīng),其Location被設(shè)置為http://news.。通過分析的web服務(wù)器日志可以得知人們離開首頁之后的去向。 我們知道重定向是如何損傷性能的,為了實現(xiàn)更好的效率,可以使用Referer日志來跟蹤內(nèi)部流量去向。每個HTTP請求都有一個Referer表示原始請求頁(除了從書簽打開或直接鍵入URL等操作),記錄下每個請求的Referer,就避免了向用戶發(fā)送重定向,從而改善了響應(yīng)時間。 2. 跟蹤出站流量有時鏈接可能將用戶帶離你的網(wǎng)站,在這種情況下,使用Referer就不太現(xiàn)實了。 同樣也可以使用重定向來解決跟蹤出站流量問題。以百度搜索為例,百度通過將每個鏈接包裝到一個302重定向來解決跟蹤的問題,例如搜索關(guān)鍵字“前端性能優(yōu)化”,搜索結(jié)果中的一個URL為https://www.baidu.com/link?url=pDjwTfa0IAf_FRBNlw1qLDtQ27YBujWp9jPN4q0QSJdNtGtDBK3ja3jyyN2CgxR5aTAywG4SI6V1NypkSyLISWjiFuFQDinhpVn4QE-uLGG&wd=&eqid=9c02bd21001c69170000000556ece297,即使搜索結(jié)果并沒有變,但這個字符串是動態(tài)改變的,暫時還不知道這里起到怎樣的作用?(個人感覺:字符串中包含了待訪問的網(wǎng)址,點擊之后會產(chǎn)生302重定向,將頁面轉(zhuǎn)到目標頁面(待修改,求大神們給我指正)) 除了重定向外,我們還可以選擇使用信標(beacon)——一個HTTP請求,其URL中包含有跟蹤信息。跟蹤信息可以從信標Web服務(wù)器的訪問日記中提取出來,信標通常是一個1px*1px的透明圖片,不過204響應(yīng)更優(yōu)秀,因為它更小,從來不被緩存,而且絕不會改變?yōu)g覽器的狀態(tài)。 十二、刪除重復(fù)腳本在團隊開發(fā)一個項目時,由于不同開發(fā)者之間都可能會向頁面中添加頁面或組件,因此可能相同的腳本會被添加多次。 重復(fù)的腳本會造成不必要的HTTP請求(如果沒有緩存該腳本的話),并且執(zhí)行多余的JavaScript浪費時間,還有可能造成錯誤。 如何避免重復(fù)腳本呢?
例如:
先檢查是否插入過,如果插入過則返回。如果該腳本依賴其它腳本,則被依賴的腳本也會被插入。最后腳本被傳送到頁面,getVersion會檢查腳本并返回追加了對應(yīng)版本號的文件名,這樣如果腳本的版本變化了,那么以前瀏覽器緩存的就會失效。 十三、配置ETag以前瀏覽器緩存的就會失效。 什么是ETag?實體標簽(EntityTag)是唯一標識了一個組件的一個特定版本的字符串,是web服務(wù)器用于確認緩存組件的有效性的一種機制,通??梢允褂媒M件的某些屬性來構(gòu)造它。 條件GET請求如果組件過期了,瀏覽器在重用它之前必須首先檢查它是否有效。瀏覽器將發(fā)送一個條件GET請求到服務(wù)器,服務(wù)器判斷緩存還有效,則發(fā)送一個304響應(yīng),告訴瀏覽器可以重用緩存組件。 那么服務(wù)器是根據(jù)什么判斷緩存是否還有效呢?有兩種方式: ETag(實體標簽); 最新修改日期; 最新修改日期原始服務(wù)器通過Last-Modified響應(yīng)頭來返回組件的最新修改日期。 舉個栗子: 當我們不帶緩存訪問www.google.com.hk的時候,我們需要下載google的logo,這時會發(fā)送這樣一個HTTP請求: Request:
Response:
Request:
Response:
實體標簽ETag提供了另外一種方式,用于檢測瀏覽器緩存中的組件與原始服務(wù)器上的組件是否匹配。摘抄自書上的例子: 不帶緩存的請求:
Response:
再次請求相同組件: Request:
Response:
為什么要引入ETag?ETag主要是為了解決Last-Modified無法解決的一些問題:
ETag帶來的問題ETag的問題在于通常使用某些屬性來構(gòu)造它,有些屬性對于特定的部署了網(wǎng)站的服務(wù)器來說是唯一的。當使用集群服務(wù)器的時候,瀏覽器從一臺服務(wù)器上獲取了原始組件,之后又向另外一臺不同的服務(wù)器發(fā)起條件GET請求,ETag就會出現(xiàn)不匹配的狀況。例如:使用inode-size-timestamp來生成ETag,文件系統(tǒng)使用inode存儲文件類型、所有者、組和訪問模式等信息,在多臺服務(wù)器上,就算文件大小、權(quán)限、時間戳等都相同,inode也是不同的。 最佳實踐
十四、使Ajax可緩存維基百科中這樣定義Ajax:
Ajax的目地是為突破web本質(zhì)的開始—停止交互方式,向用戶顯示一個白屏后重繪整個頁面不是一種好的用戶體驗。 異步與即時Ajax的一個明顯的有點就是向用戶提供了即時反饋,因為它異步的從后端web服務(wù)器請求信息。 用戶是否需要等待的關(guān)鍵因素在于Ajax請求是被動的還是主動的。被動請求是為了將來來使用而預(yù)先發(fā)起的,主動請求是基于用戶當前的操作而發(fā)起的 什么樣的AJAX請求可以被緩存?POST的請求,是不可以在客戶端緩存的,每次請求都需要發(fā)送給服務(wù)器進行處理,每次都會返回狀態(tài)碼200。(可以在服務(wù)器端對數(shù)據(jù)進行緩存,以便提高處理速度) GET的請求,是可以(而且默認)在客戶端進行緩存的,除非指定了不同的地址,否則同一個地址的AJAX請求,不會重復(fù)在服務(wù)器執(zhí)行,而是返回304。 Ajax請求使用緩存在進行Ajax請求的時候,可以選擇盡量使用get方法,這樣可以使用客戶端的緩存,提高請求速度 |
|
|
來自: CoCO-Ebook > 《前端優(yōu)化》