如何理解 Web 性能優(yōu)化
事實(shí)上就是用戶覺(jué)得頁(yè)面加載很快誉裆,用戶從輸入U(xiǎn)RL(網(wǎng)址)到頁(yè)面在瀏覽器上加載出來(lái)的時(shí)間很短黑竞;與之相對(duì)的有如服務(wù)器性能優(yōu)化(如網(wǎng)頁(yè)占的 CPU 少)休里,一定要區(qū)分開來(lái)澄干。
對(duì)于用戶眾多的網(wǎng)站,節(jié)約下的加載時(shí)間或能帶來(lái)可觀的收入罐柳,這便是前端 Web 性能優(yōu)化的意義锻狗。
從輸入 URL 到頁(yè)面加載發(fā)生了什么
一道所有前端耳熟能詳?shù)慕?jīng)典面試題满力,也確實(shí)是需要前端去深入研究的東西。下面我會(huì)簡(jiǎn)單介紹其過(guò)程轻纪,并羅列相關(guān)的 Web 優(yōu)化方案油额。
0. 緩存
當(dāng)我們?cè)跒g覽器上輸入網(wǎng)址,瀏覽器首先會(huì)查看是否有緩存刻帚,如果之前已經(jīng)訪問(wèn)過(guò)該網(wǎng)站潦嘶,則會(huì)有緩存,那瀏覽器就不必再向服務(wù)器發(fā)請(qǐng)求了崇众,用戶則能夠很快得看到內(nèi)容掂僵。Web 性能優(yōu)化有極大一部分都是優(yōu)化緩存,緩存事實(shí)上又分為數(shù)據(jù)庫(kù)緩存顷歌、代理服務(wù)器緩存锰蓬、還有我們熟悉的 CDN 緩存,以及瀏覽器緩存等眯漩,部分內(nèi)容后文介紹芹扭。
1. DNS 查詢
DNS 查詢就像電話簿,你在瀏覽器地址欄輸入網(wǎng)址赦抖,通過(guò) DNS 查詢得到域名的真實(shí) IP舱卡。
DNS查詢完成之前,瀏覽器無(wú)法從服務(wù)器下載任何數(shù)據(jù)队萤。
優(yōu)化方案:減少 DNS 查詢
1.1 DNS 緩存
ISP轮锥、局域網(wǎng)、操作系統(tǒng)要尔、瀏覽器等都會(huì)有相應(yīng)的DNS緩存機(jī)制交胚。
1.2 減少頁(yè)面的唯一域名
因?yàn)槊看?DNS 查詢就是查找唯一域名的過(guò)程,那么域名越少盈电,DNS 查詢就越少,應(yīng)該盡量將資源放在同一域名杯活。當(dāng)然這樣做又有其他問(wèn)題匆帚,下文詳解。
2. TCP 連接
經(jīng)典的三次握手和四次揮手旁钧,不展開贅述吸重。
簡(jiǎn)單講講優(yōu)化方案:TCP 連接復(fù)用(TCP Connection Reuse)互拾,在 HTTP 請(qǐng)求頭中的 Connection 上加 keep-alive;HTTP/2.0 多路復(fù)用等嚎幸。
3. HTTP 請(qǐng)求及響應(yīng)
直接講優(yōu)化策略
3.1 避免不必要的重定向
最浪費(fèi)的重定向經(jīng)常發(fā)生颜矿、而且很容易被忽略:URL 末尾應(yīng)該添加/但未添加。比如嫉晶,訪問(wèn)http://astrology.yahoo.com/astrology將被301重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)骑疆。如果使用 Apache,可以通過(guò)Alias或mod_rewrite或DirectorySlash解決這個(gè)問(wèn)題替废。
3.2 Cookie
3.2.1減少 Cookie 大小
每次請(qǐng)求都會(huì)帶上對(duì)應(yīng)的 Cookie箍铭,減少 Cookie 大小可以降低其對(duì)響應(yīng)速度的影響:
- 去除不必要的 Cookie;
- 盡量壓縮 Cookie 大凶盗汀诈火;
- 注意設(shè)置 Cookie 的 domain 級(jí)別,如無(wú)必要状答,不要影響到 sub-domain冷守;
- 設(shè)置合適的過(guò)期時(shí)間。
3.2.2 靜態(tài)資源使用無(wú) Cookie 域名
靜態(tài)資源一般無(wú)需使用 Cookie惊科,可以把它們放在使用二級(jí)域名或者專門域名的無(wú) Cookie 服務(wù)器上拍摇,降低 Cookie 傳送的造成的流量浪費(fèi),提高響應(yīng)速度译断。
3.3 添加 Expires 或 Cache-Control 響應(yīng)頭
HTTP/1.1 增加的 Cache-Control授翻,它比 Expires 等好在其設(shè)定時(shí)間是相對(duì)的,避免了用戶本地設(shè)置時(shí)間落后所造成的無(wú)法良好緩存的問(wèn)題等孙咪。
- 靜態(tài)內(nèi)容:將 Expires 響應(yīng)頭設(shè)置為將來(lái)很遠(yuǎn)的時(shí)間堪唐,實(shí)現(xiàn)「永不過(guò)期」策略;
- 動(dòng)態(tài)內(nèi)容:設(shè)置合適的 Cache-Control 響應(yīng)頭翎蹈,讓瀏覽器有條件地發(fā)起請(qǐng)求淮菠。
3.4 配置 Etag
通過(guò)如 MD5 等加密算法,設(shè)置緩存體的 Etag 配合 3.3 的緩存時(shí)間使用荤堪,這樣 Cache-Control 就可以設(shè)置較長(zhǎng)時(shí)間(max-age 設(shè)置個(gè)十年半載 )合陵,只要瀏覽器緩存中資源與源服務(wù)器中的資源 Etag 不一致,說(shuō)明內(nèi)容更新了澄阳,此時(shí)再下載新資源拥知;Etag 匹配成功則直接響應(yīng) 304,不用重復(fù)下載了用戶自然感覺(jué)很快碎赢。
3.5 使用 Gzip
使用 Gzip 就是將 HTML低剔、CSS、JS、XML襟齿、JSON 等資源進(jìn)行 Gzip 高效壓縮姻锁,減少資源體積那么下載就會(huì)更快。
Gzip 壓縮通巢缕郏可以減少 70% 的響應(yīng)大小位隶,對(duì)某些文件更可能高達(dá) 90%,比 Deflate 更高效开皿。主流 Web 服務(wù)器都有相應(yīng)模塊涧黄,而且絕大多數(shù)瀏覽器支持 Gzip 解碼。
從HTTP/1.1開始副瀑,客戶端就有了支持壓縮的 Accept-Encoding HTTP 請(qǐng)求頭弓熏。
Accept-Encoding: gzip, deflate
服務(wù)器看到這個(gè)請(qǐng)求頭,它就會(huì)用客戶端列出的一種方式來(lái)壓縮響應(yīng)糠睡。web服務(wù)器通過(guò) Content-Encoding 響應(yīng)頭來(lái)通知客戶端挽鞠。
Content-Encoding: gzip
需要注意的是,已經(jīng)壓縮過(guò)的內(nèi)容如圖片和PDF不要使用 Gzip狈孔,另外還有文件內(nèi)容本身就很小信认,這些資源再使用 Gzip 反而會(huì)增加資源下載時(shí)間,浪費(fèi) CPU 資源均抽,而且還可能增加文件體積嫁赏。
值得一提
HTTP 請(qǐng)求的另一個(gè)優(yōu)化方案是增加同時(shí)請(qǐng)求的數(shù)量,瀏覽器會(huì)同時(shí)發(fā)送多個(gè)請(qǐng)求油挥,但是同一域名最多同時(shí)發(fā)送 4~8 個(gè)(不同瀏覽器不同)請(qǐng)求潦蝇,那么當(dāng)資源過(guò)多時(shí),可以采用增加域名的方法增加并發(fā)量深寥。當(dāng)然這一方法又與上述 DNS 查詢的優(yōu)化方案矛盾攘乒,真正使用的時(shí)候就需要權(quán)衡。
另外惋鹅,既然一次只能發(fā)的請(qǐng)求有限则酝,就應(yīng)該將重要的需要優(yōu)先展示的資源先請(qǐng)求:
3.6 延遲加載(懶加載)
頁(yè)面初始加載時(shí)哪些內(nèi)容是絕對(duì)必需的?不在答案之列的資源都可以延遲加載闰集。比如:
- 非首屏使用的數(shù)據(jù)沽讹、樣式、腳本武鲁、圖片等爽雄;
- 用戶交互時(shí)才會(huì)顯示的內(nèi)容。
遵循「漸進(jìn)增強(qiáng)」理念開發(fā)的網(wǎng)站:JavaScript用于增強(qiáng)用用戶體驗(yàn)沐鼠,但沒(méi)有(不支持) JavaScript也能正常工作盲链,完全可以延遲加載JavaScript。
將首屏以外的HTML放在不渲染的元素中,如隱藏的<textarea>刽沾,或者type屬性為非執(zhí)行腳本的<script>標(biāo)簽中,減少初始渲染的DOM元素?cái)?shù)量排拷,提高速度侧漓。等首屏加載完成或者用戶操作時(shí),再去渲染剩余的頁(yè)面內(nèi)容监氢。
3.7 預(yù)加載
預(yù)先加載利用瀏覽器空閑時(shí)間請(qǐng)求將來(lái)要使用的資源布蔗,以便用戶訪問(wèn)下一頁(yè)面時(shí)更快地響應(yīng)。
4. 瀏覽器解析渲染頁(yè)面
響應(yīng)完成后浪腐,瀏覽器下載完資源纵揍,就開始解析資源生成頁(yè)面了。對(duì)于前端來(lái)說(shuō)议街,這部分內(nèi)容是完全需要我們?nèi)フ瓶氐脑蠼鳎覀円瞾?lái)簡(jiǎn)單介紹一下對(duì)應(yīng)的優(yōu)化內(nèi)容,部分內(nèi)容如懶加載等上面已經(jīng)提及就不再重復(fù)特漩。
4.1 寫對(duì)文檔類型聲明 <!DOCTYPE html>
這個(gè)聲明的目的是防止瀏覽器在渲染文檔時(shí)吧雹,切換到我們稱為“怪異模式(兼容模式)”的渲染模式⊥可恚“
<!DOCTYPE html>
" 確保瀏覽器按照最佳的相關(guān)規(guī)范進(jìn)行渲染雄卷,而不是使用一個(gè)不符合規(guī)范的渲染模式。
不寫或?qū)戝e(cuò)文檔類型聲明蛤售,會(huì)浪費(fèi)瀏覽器渲染頁(yè)面的時(shí)間或引起錯(cuò)誤排版丁鹉。
4.2 CSS 放在 <head> 中
把樣式表放在 <head> 中可以讓頁(yè)面漸進(jìn)渲染,盡早呈現(xiàn)視覺(jué)反饋悴能,給用戶加載速度很快的感覺(jué)揣钦。
這對(duì)內(nèi)容比較多的頁(yè)面尤為重要,用戶可以先查看已經(jīng)下載渲染的內(nèi)容搜骡,而不是盯著白屏等待拂盯。
如果把樣式表放在頁(yè)面底部,一些瀏覽器為減少重繪记靡,會(huì)在 CSS 加載完成以后才渲染頁(yè)面谈竿,用戶只能對(duì)著白屏干瞪眼,用戶體驗(yàn)極差摸吠。把樣式表放到文檔的HEAD部分能讓頁(yè)面看起來(lái)加載地更快空凸。
4.2 把腳本放在頁(yè)面底部
瀏覽器下載腳本時(shí),會(huì)阻塞其他資源并行下載寸痢,即使是來(lái)自不同域名的資源呀洲,并且,沒(méi)有 js 并不郵箱呈現(xiàn)在用戶目前的內(nèi)容的觀感。因此道逗,最好將腳本放在底部兵罢,以提高頁(yè)面加載速度。
一些特殊場(chǎng)景無(wú)法將腳本放到頁(yè)面底部的滓窍,可以考慮<script>的以下屬性:
- defer 屬性卖词;
- HTML5 新增的async屬性。
4.3 使用外部 JavaScript 和 CSS
外部 JavaScript 和 CSS 文件可以被瀏覽器緩存吏夯,在不同頁(yè)面間重用此蜈,也能降低頁(yè)面大小。
當(dāng)然噪生,實(shí)際中也需要考慮代碼的重用程度裆赵。如果僅僅是某個(gè)頁(yè)面使用到的代碼,可以考慮內(nèi)嵌在頁(yè)面中跺嗽,減少HTTP請(qǐng)求數(shù)战授。另外,可以在首頁(yè)加載完成以后抛蚁,預(yù)先加載子頁(yè)面的資源陈醒。
4.4 合并和壓縮 JS/CSS 等文件
通過(guò)該方法減少頁(yè)面所需資源,減少請(qǐng)求數(shù)量瞧甩,加快響應(yīng)時(shí)間《危現(xiàn)在 webpack 打包工具都已經(jīng)默認(rèn)實(shí)現(xiàn)了。
4.5 減少 DOM 操作和使用高效的事件處理
- 緩存已經(jīng)訪問(wèn)過(guò)的元素肚逸;
- 使用 DocumentFragment 暫存 DOM爷辙,整理好以后再插入 DOM 樹;
- 操作 className朦促,而不是多次讀寫 style膝晾;
- 避免使用 JavaScript 修復(fù)布局;
- 減少綁定事件監(jiān)聽(tīng)的節(jié)點(diǎn)务冕,如通過(guò)事件委托(當(dāng)然現(xiàn)在瀏覽器功能強(qiáng)大血当,影響不大);
- 盡早處理事件禀忆,在 DOMContentLoaded 即可進(jìn)行臊旭,不用等到 load 以后。
4.6 圖片優(yōu)化
如何將圖片變得又小又好看是一個(gè)工程師實(shí)力的體現(xiàn)箩退,這里不過(guò)多贅述离熏,大家可以查看我后文提供的資源。
4.7 使用 CND
內(nèi)容分發(fā)網(wǎng)絡(luò)(Content delivery network 或 Content distribution network)是指一種透過(guò)互聯(lián)網(wǎng)互相連接的計(jì)算機(jī)網(wǎng)絡(luò)系統(tǒng)戴涝,利用最靠近每位用戶的服務(wù)器滋戳,更快钻蔑、更可靠地將音樂(lè)、圖片奸鸯、影片咪笑、應(yīng)用程序及其他文件發(fā)送給用戶,來(lái)提供高性能娄涩、可擴(kuò)展性及低成本的網(wǎng)絡(luò)內(nèi)容傳遞給用戶蒲肋。
動(dòng)態(tài) CDN,使用離你最近的服務(wù)器钝满;CDN 沒(méi)有 Cookie,使用 CDN 可以減少 Cookie申窘;CND 會(huì)自動(dòng)合并腳本文件等弯蚜,減少請(qǐng)求數(shù)量;當(dāng)然剃法,使用 CND 同時(shí)也增加了一個(gè)域名碎捺,增大了同時(shí)請(qǐng)求數(shù)量。
總結(jié)
該文大量參考了雅虎 35 軍規(guī)贷洲,增加了一些自己的理解并舍棄了一些已經(jīng)過(guò)時(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í)上是貫穿在整個(gè)過(guò)程當(dāng)中的钦椭,正如 Web 性能優(yōu)化是個(gè)整體拧额,需要權(quán)衡所有沖突。希望本文可以給你一些在面試官問(wèn)道你時(shí)的思路彪腔。
深入閱讀 從輸入U(xiǎn)RL到頁(yè)面加載的過(guò)程侥锦?如何由一道題完善自己的前端知識(shí)體系!
本文參考:
前端性能優(yōu)化之雅虎35條軍規(guī)
前端經(jīng)典面試題: 從輸入U(xiǎn)RL到頁(yè)面加載發(fā)生了什么德挣?
MDN
維基百科