《高效能網站建設指南》筆記
緒言A:前端效能的重要性
效能黃金法則:只有10%~20%的終端使用者響應時間花在了下載HTML文件上,其餘80%~90%時間花在了下載頁面中的所有元件上。
規則1:減少HTTP請求
圖片地圖
圖片地圖允許在一個圖片上關聯多個URL,目標URL的選擇取決於使用者點選了圖片上的哪個位置。
圖片地圖有兩種型別
- 伺服器端圖片地圖:將使用者點選提交到服務端,並向其傳遞使用者點選的x、y座標。Web應用程式將x、y對映為適當的操作。
- 客戶端圖片地圖:通過HTML的MAP標籤實現。
CSS Sprites
任何支援背景圖片的HTML元素都能CSS Sprites,通過background-image和background-position來實現。
內聯圖片
通過使用data:URL模式可以在Web頁面中包含圖片但無需任何額外的HTTP請求。
data:[<mediatype>][;base64],<data>
由於data:URL是內聯在頁面中的,在跨越不同頁面時不會被快取
合併指令碼和樣式表
規則2:使用內容釋出網路(CDN)
如果應用程式Web伺服器離使用者更近,則一個HTTP請求響應時間將縮短。另一方面,如果元件Web伺服器離使用者更近,則多個HTTP請求的響應時間將縮短。
內容釋出網路
內容釋出網路是一組分佈在多個不同地理位置的Web伺服器,用於更加有效地向用戶釋出內容。
優點:
- 縮短響應時間
- CDN包括備份、擴充套件儲存能力和進行快取等能力,有助於緩和Web流量峰值壓力。
缺點:
CDN服務提供商在其所有客戶之間共享其Web伺服器
無法直接控制組件伺服器所帶來的特殊麻煩
規則3:新增Expires頭
Expires頭
瀏覽器(和代理)使用快取來減少HTTP請求的數量,並減小HTTP響應的大小,是Web頁面載入得更快。
Web伺服器使用Expires頭來告訴Web客戶端它可以使用一個元件的當前副本,直到指定的時間為止。
HTTP簡要地稱該頭為“在這一日期/時間之後,響應被認為是無效的”
Max-Age
HTTP1.1引入了Cache-Control頭來客服Expires頭的限制。因為Expires頭使用一個特定的時間,它要求伺服器和客戶端的時鐘嚴格同步。另外,過期日期需要經常檢查,並且一旦未來這一天到來,還需要在伺服器中配置一個新的日期。
Cache-Control使用max-age指令指定元件被快取多久,它以秒為單位定義了一個更新窗。
可以同時指定Expires和Cache-Control max-age,如果兩者同時出現,HTTP規範規定max-age指令將重寫Expires頭。
如果一個元件沒有長久的Expire頭,它仍然會儲存在瀏覽器的快取中。在後續請求中,瀏覽器會檢查快取並發現元件已經過期。為了提高效率,瀏覽器會向原始伺服器傳送一個條件GET請求,如果元件沒有改變,原始伺服器可以免於傳送整個元件,而是傳送一個很小的投,告訴瀏覽器可以使用其快取的元件。
規則4:壓縮元件
壓縮是如何工作的
Web客戶端可以通過HTTP請求中的Accept-Encoding頭來表示對壓縮的支援
Accept-Encoding: gzip, deflate
如果Web伺服器看到請求中有這個頭,就會使用客戶端列出的方法中的一種來壓縮響應,Web伺服器通過響應中的Content-Control來通知Web客戶端。
Content-Encoding: gzip
壓縮什麼
基於文字的資源如html,js,css,xml都適用於壓縮。然而對於圖片而言,卻不應該對圖片進行壓縮,因為圖片本身是已經被壓縮過了,如果再進行gzip壓縮,有可能得到的結果是和圖片本身大小相差不大或更大,這樣就浪費了伺服器的CPU資源來做無用功了。
節省
壓縮通常能將響應的資料量減少70%。
代理快取
首先,假設到達代理的是一個來自不支援gzip的瀏覽器的請求,代理會將請求轉發到web伺服器,此時web伺服器的響應是未經過壓縮的,這個響應會把代理伺服器快取起來併發給瀏覽器。現在,假設到達代理的第二個請求來自一個支援gzip瀏覽器,請求的是與之前相同的URL,代理會直接使用未經壓縮的快取響應,那麼久失去了進行壓縮的機會了。考慮更糟糕的情況,第一個請求來自支援gzip的瀏覽器,第二個請求來自不支援gzip的瀏覽器,這樣第二個請求得到的快取響應將無法被解碼,導致出錯。
解決這一問題的方法是在Web伺服器的響應中新增Vary頭。Web伺服器可以告訴代理根據一個或多個頭來改變快取的響應。
Vary: Accept-Encoding
規則5:將樣式表放在頂部
將樣式表放在文件底部會導致在瀏覽器中阻止內容逐步呈現。為避免當樣式變化時重繪頁面中的元素,瀏覽器會阻塞內容逐步呈現。
將CSS放在頂部
可以通過link標籤或者@import規則引入樣式。
一個STYLE塊可以包含多個@import規則,但是@import規則必須放在其他所有規則之前。使用link標籤除了語法簡單外,相對於@Import規則,還能帶來效能上的收益:@import規則可能會導致白屏,並且使用@import規則會導致元件下載時的無序性。
無樣式內容閃爍
如果樣式表扔在載入,構建呈現樹就是一種浪費,因為在所有樣式表載入並解析完畢之前無需繪製任何東西。否則,在其準備好之前顯示內容會遇到FOUC(無樣式內容的閃爍,Flash of Unstyled Content)問題。
閃爍並不總是發生,它取決於你的瀏覽器以及如何載入頁面,可能會選擇白屏,也可能選擇承擔FOUC風險。
規則6:將指令碼放在底部
需要注意,瀏覽器對每個主機並行下載的元件數量是有限制的。
規則7:避免CSS表示式
規則8:使用外部JavaScript和CSS
純粹而言,內聯快一些。因為外部示例需要承擔多個HTTP請求帶來的開銷。儘管如此,現實中還是外部檔案較快。因為JS和CSS檔案有機會被瀏覽器快取起來。
兩全其美的方法:
- 載入後下載:作為多次頁面瀏覽量中的第一次的主頁,我們希望為主頁內聯JavaScript和CSS,但又能為所有後續頁面瀏覽量提供外部檔案。這可以通過在主頁載入完成後動態下載外部元件來實現(通過onload事件)。這能夠將外部檔案放到瀏覽器中的快取中以便使用者接下來訪問其他頁面。
- 動態內聯:如果主頁伺服器知道一個元件是否在瀏覽器的快取中,它可以在內聯或使用外部檔案之間做出最佳的選擇。儘管伺服器不能檢視瀏覽器快取中有什麼,但可以用cookies做指示器。如果cookies不存在,就內聯js和css。如果cookie出現了,則可能外部元件位於瀏覽器的快取中,並使用了外部檔案。
規則9:減少DNS查詢
減少唯一主機的數量會潛在地減少頁中並行下載的數量。避免DNS查詢降低了響應時間,但減少並行下載可能會增加響應時間。·所以建議主機名至少2個,但不要超過4個。
規則10:精簡JavaScript
規則11:避免重定向
規則12:刪除重複指令碼
規則13:配置ETag
ETag是什麼
實體標籤(Entity Tag,ETag)是Web伺服器和瀏覽器用於確認快取元件有效性的一種機制。
條件GET請求
當瀏覽器快取的元件過期了(或者使用者明確地重新載入了頁面),瀏覽器在重用它之前必須首先檢查它是否仍然有效,這稱作一個條件GET請求。伺服器在檢查快取的元件是否和原始伺服器上的元件匹配時有兩種方式:
- 比較最新修改日期
- 比較實體標籤
實體標籤
ETag是HTTP1.1中引入。ETag是唯一標識了一個元件的一個特定版本的字串,唯一的格式約束是該字串必須用引號引起來。
ETag的加入為驗證實體提供了比Last-Modified更靈活機制,如果實體依據User-Agent和Accept-Language頭而改變,實體的狀態可以反映在ETag中。
如果瀏覽器必須驗證一個元件,它會使用If-None-Match頭將ETag傳回原始伺服器,如果ETag匹配,就會返回304狀態碼。
ETag帶來的問題
通常使用元件的某些屬性來構造,這些屬性對於特定的、寄宿了網站的伺服器來說是唯一的。當瀏覽器從一臺伺服器上獲取了原始元件,之後,又向另一臺不同的伺服器發起條件GET請求時,ETag是不會匹配的。
ETag還降低了代理快取的效率。代理後面使用者快取的ETag經常和代理快取的ETag不匹配,這導致不必要的請求傳送到原始伺服器。
如果ETag不匹配,使用者就會收到普通的200響應以及元件的所有資料。而對元件進行不必要的重新載入還會影響伺服器的效能並增加頻寬開銷。
If-None-Match比If-Modified-Since具有更高的優先順序,如果請求中同時出現了這兩個頭,原始伺服器禁止返回304,除非請求中的條件欄位全部一致。
ETag——用還是不用
如果是唯一伺服器,使用ETag沒有任何問題,但是如果是多臺伺服器,最好可以自定義ETag:ETag中將只包含大小和時間戳。然而,因為這些都是重複資訊,所以最好將ETag完全移除——Last Modified可以提供完全等價的資訊。