如何理解 Web 性能優(yōu)化
事實(shí)上就是用戶覺得頁面加載很快,用戶從輸入U(xiǎn)RL(網(wǎng)址)到頁面在瀏覽器上加載出來的時(shí)間很短;與之相對的有如服務(wù)器性能優(yōu)化(如網(wǎng)頁占的 CPU 少),一定要區(qū)分開來。
對于用戶眾多的網(wǎng)站,節(jié)約下的加載時(shí)間或能帶來可觀的收入,這便是前端 Web 性能優(yōu)化的意義。
從輸入 URL 到頁面加載發(fā)生了什么
一道所有前端耳熟能詳?shù)慕?jīng)典面試題,也確實(shí)是需要前端去深入研究的東西。下面我會簡單介紹其過程,并羅列相關(guān)的 Web 優(yōu)化方案。
0. 緩存
當(dāng)我們在瀏覽器上輸入網(wǎng)址,瀏覽器首先會查看是否有緩存,如果之前已經(jīng)訪問過該網(wǎng)站,則會有緩存,那瀏覽器就不必再向服務(wù)器發(fā)請求了,用戶則能夠很快得看到內(nèi)容。Web 性能優(yōu)化有極大一部分都是優(yōu)化緩存,緩存事實(shí)上又分為數(shù)據(jù)庫緩存、代理服務(wù)器緩存、還有我們熟悉的 CDN 緩存,以及瀏覽器緩存等,部分內(nèi)容后文介紹。
1. DNS 查詢
DNS 查詢就像電話簿,你在瀏覽器地址欄輸入網(wǎng)址,通過 DNS 查詢得到域名的真實(shí) IP。

DNS查詢完成之前,瀏覽器無法從服務(wù)器下載任何數(shù)據(jù)。
優(yōu)化方案:減少 DNS 查詢
1.1 DNS 緩存
ISP、局域網(wǎng)、操作系統(tǒng)、瀏覽器等都會有相應(yīng)的DNS緩存機(jī)制。
1.2 減少頁面的唯一域名
因?yàn)槊看?DNS 查詢就是查找唯一域名的過程,那么域名越少,DNS 查詢就越少,應(yīng)該盡量將資源放在同一域名。當(dāng)然這樣做又有其他問題,下文詳解。
2. TCP 連接
經(jīng)典的三次握手和四次揮手,不展開贅述。
簡單講講優(yōu)化方案:TCP 連接復(fù)用(TCP Connection Reuse),在 HTTP 請求頭中的 Connection 上加 keep-alive;HTTP/2.0 多路復(fù)用等。
3. HTTP 請求及響應(yīng)
直接講優(yōu)化策略
3.1 避免不必要的重定向
最浪費(fèi)的重定向經(jīng)常發(fā)生、而且很容易被忽略:URL 末尾應(yīng)該添加/但未添加。比如,訪問http://astrology.yahoo.com/astrology將被301重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。如果使用 Apache,可以通過Alias或mod_rewrite或DirectorySlash解決這個問題。
3.2 Cookie
3.2.1減少 Cookie 大小
每次請求都會帶上對應(yīng)的 Cookie,減少 Cookie 大小可以降低其對響應(yīng)速度的影響:
- 去除不必要的 Cookie;
- 盡量壓縮 Cookie 大小;
- 注意設(shè)置 Cookie 的 domain 級別,如無必要,不要影響到 sub-domain;
- 設(shè)置合適的過期時(shí)間。
3.2.2 靜態(tài)資源使用無 Cookie 域名
靜態(tài)資源一般無需使用 Cookie,可以把它們放在使用二級域名或者專門域名的無 Cookie 服務(wù)器上,降低 Cookie 傳送的造成的流量浪費(fèi),提高響應(yīng)速度。
3.3 添加 Expires 或 Cache-Control 響應(yīng)頭
HTTP/1.1 增加的 Cache-Control,它比 Expires 等好在其設(shè)定時(shí)間是相對的,避免了用戶本地設(shè)置時(shí)間落后所造成的無法良好緩存的問題等。
- 靜態(tài)內(nèi)容:將 Expires 響應(yīng)頭設(shè)置為將來很遠(yuǎn)的時(shí)間,實(shí)現(xiàn)「永不過期」策略;
- 動態(tài)內(nèi)容:設(shè)置合適的 Cache-Control 響應(yīng)頭,讓瀏覽器有條件地發(fā)起請求。
3.4 配置 Etag
通過如 MD5 等加密算法,設(shè)置緩存體的 Etag 配合 3.3 的緩存時(shí)間使用,這樣 Cache-Control 就可以設(shè)置較長時(shí)間(max-age 設(shè)置個十年半載 ),只要瀏覽器緩存中資源與源服務(wù)器中的資源 Etag 不一致,說明內(nèi)容更新了,此時(shí)再下載新資源;Etag 匹配成功則直接響應(yīng) 304,不用重復(fù)下載了用戶自然感覺很快。
3.5 使用 Gzip
使用 Gzip 就是將 HTML、CSS、JS、XML、JSON 等資源進(jìn)行 Gzip 高效壓縮,減少資源體積那么下載就會更快。
Gzip 壓縮通??梢詼p少 70% 的響應(yīng)大小,對某些文件更可能高達(dá) 90%,比 Deflate 更高效。主流 Web 服務(wù)器都有相應(yīng)模塊,而且絕大多數(shù)瀏覽器支持 Gzip 解碼。
從HTTP/1.1開始,客戶端就有了支持壓縮的 Accept-Encoding HTTP 請求頭。
Accept-Encoding: gzip, deflate
服務(wù)器看到這個請求頭,它就會用客戶端列出的一種方式來壓縮響應(yīng)。web服務(wù)器通過 Content-Encoding 響應(yīng)頭來通知客戶端。
Content-Encoding: gzip
需要注意的是,已經(jīng)壓縮過的內(nèi)容如圖片和PDF不要使用 Gzip,另外還有文件內(nèi)容本身就很小,這些資源再使用 Gzip 反而會增加資源下載時(shí)間,浪費(fèi) CPU 資源,而且還可能增加文件體積。
值得一提
HTTP 請求的另一個優(yōu)化方案是增加同時(shí)請求的數(shù)量,瀏覽器會同時(shí)發(fā)送多個請求,但是同一域名最多同時(shí)發(fā)送 4~8 個(不同瀏覽器不同)請求,那么當(dāng)資源過多時(shí),可以采用增加域名的方法增加并發(fā)量。當(dāng)然這一方法又與上述 DNS 查詢的優(yōu)化方案矛盾,真正使用的時(shí)候就需要權(quán)衡。
另外,既然一次只能發(fā)的請求有限,就應(yīng)該將重要的需要優(yōu)先展示的資源先請求:
3.6 延遲加載(懶加載)
頁面初始加載時(shí)哪些內(nèi)容是絕對必需的?不在答案之列的資源都可以延遲加載。比如:
- 非首屏使用的數(shù)據(jù)、樣式、腳本、圖片等;
- 用戶交互時(shí)才會顯示的內(nèi)容。
遵循「漸進(jìn)增強(qiáng)」理念開發(fā)的網(wǎng)站:JavaScript用于增強(qiáng)用用戶體驗(yàn),但沒有(不支持) JavaScript也能正常工作,完全可以延遲加載JavaScript。
將首屏以外的HTML放在不渲染的元素中,如隱藏的<textarea>,或者type屬性為非執(zhí)行腳本的<script>標(biāo)簽中,減少初始渲染的DOM元素?cái)?shù)量,提高速度。等首屏加載完成或者用戶操作時(shí),再去渲染剩余的頁面內(nèi)容。
3.7 預(yù)加載
預(yù)先加載利用瀏覽器空閑時(shí)間請求將來要使用的資源,以便用戶訪問下一頁面時(shí)更快地響應(yīng)。
4. 瀏覽器解析渲染頁面
響應(yīng)完成后,瀏覽器下載完資源,就開始解析資源生成頁面了。對于前端來說,這部分內(nèi)容是完全需要我們?nèi)フ瓶氐?,我們也來簡單介紹一下對應(yīng)的優(yōu)化內(nèi)容,部分內(nèi)容如懶加載等上面已經(jīng)提及就不再重復(fù)。
4.1 寫對文檔類型聲明 <!DOCTYPE html>
這個聲明的目的是防止瀏覽器在渲染文檔時(shí),切換到我們稱為“怪異模式(兼容模式)”的渲染模式?!?code><!DOCTYPE html>" 確保瀏覽器按照最佳的相關(guān)規(guī)范進(jìn)行渲染,而不是使用一個不符合規(guī)范的渲染模式。
不寫或?qū)戝e文檔類型聲明,會浪費(fèi)瀏覽器渲染頁面的時(shí)間或引起錯誤排版。
4.2 CSS 放在 <head> 中
把樣式表放在 <head> 中可以讓頁面漸進(jìn)渲染,盡早呈現(xiàn)視覺反饋,給用戶加載速度很快的感覺。
這對內(nèi)容比較多的頁面尤為重要,用戶可以先查看已經(jīng)下載渲染的內(nèi)容,而不是盯著白屏等待。
如果把樣式表放在頁面底部,一些瀏覽器為減少重繪,會在 CSS 加載完成以后才渲染頁面,用戶只能對著白屏干瞪眼,用戶體驗(yàn)極差。把樣式表放到文檔的HEAD部分能讓頁面看起來加載地更快。
4.2 把腳本放在頁面底部
瀏覽器下載腳本時(shí),會阻塞其他資源并行下載,即使是來自不同域名的資源,并且,沒有 js 并不郵箱呈現(xiàn)在用戶目前的內(nèi)容的觀感。因此,最好將腳本放在底部,以提高頁面加載速度。
一些特殊場景無法將腳本放到頁面底部的,可以考慮<script>的以下屬性:
- defer 屬性;
- HTML5 新增的async屬性。
4.3 使用外部 JavaScript 和 CSS
外部 JavaScript 和 CSS 文件可以被瀏覽器緩存,在不同頁面間重用,也能降低頁面大小。
當(dāng)然,實(shí)際中也需要考慮代碼的重用程度。如果僅僅是某個頁面使用到的代碼,可以考慮內(nèi)嵌在頁面中,減少HTTP請求數(shù)。另外,可以在首頁加載完成以后,預(yù)先加載子頁面的資源。
4.4 合并和壓縮 JS/CSS 等文件
通過該方法減少頁面所需資源,減少請求數(shù)量,加快響應(yīng)時(shí)間。現(xiàn)在 webpack 打包工具都已經(jīng)默認(rèn)實(shí)現(xiàn)了。
4.5 減少 DOM 操作和使用高效的事件處理
- 緩存已經(jīng)訪問過的元素;
- 使用 DocumentFragment 暫存 DOM,整理好以后再插入 DOM 樹;
- 操作 className,而不是多次讀寫 style;
- 避免使用 JavaScript 修復(fù)布局;
- 減少綁定事件監(jiān)聽的節(jié)點(diǎn),如通過事件委托(當(dāng)然現(xiàn)在瀏覽器功能強(qiáng)大,影響不大);
- 盡早處理事件,在 DOMContentLoaded 即可進(jìn)行,不用等到 load 以后。
4.6 圖片優(yōu)化
如何將圖片變得又小又好看是一個工程師實(shí)力的體現(xiàn),這里不過多贅述,大家可以查看我后文提供的資源。
4.7 使用 CND
內(nèi)容分發(fā)網(wǎng)絡(luò)(Content delivery network 或 Content distribution network)是指一種透過互聯(lián)網(wǎng)互相連接的計(jì)算機(jī)網(wǎng)絡(luò)系統(tǒng),利用最靠近每位用戶的服務(wù)器,更快、更可靠地將音樂、圖片、影片、應(yīng)用程序及其他文件發(fā)送給用戶,來提供高性能、可擴(kuò)展性及低成本的網(wǎng)絡(luò)內(nèi)容傳遞給用戶。
動態(tài) CDN,使用離你最近的服務(wù)器;CDN 沒有 Cookie,使用 CDN 可以減少 Cookie;CND 會自動合并腳本文件等,減少請求數(shù)量;當(dāng)然,使用 CND 同時(shí)也增加了一個域名,增大了同時(shí)請求數(shù)量。
總結(jié)

該文大量參考了雅虎 35 軍規(guī),增加了一些自己的理解并舍棄了一些已經(jīng)過時(shí)的內(nèi)容。細(xì)節(jié)內(nèi)容比較少,主要是籠統(tǒng)地將 Web 性能優(yōu)化的思路做了梳理,很多內(nèi)容都值得我們?nèi)ド钊胙芯俊.?dāng)然其中部分內(nèi)容順序還是不佳,因?yàn)楹芏鄡?nèi)容事實(shí)上是貫穿在整個過程當(dāng)中的,正如 Web 性能優(yōu)化是個整體,需要權(quán)衡所有沖突。希望本文可以給你一些在面試官問道你時(shí)的思路。
深入閱讀 從輸入U(xiǎn)RL到頁面加載的過程?如何由一道題完善自己的前端知識體系!
本文參考:
前端性能優(yōu)化之雅虎35條軍規(guī)
前端經(jīng)典面試題: 從輸入U(xiǎn)RL到頁面加載發(fā)生了什么?
MDN
維基百科