Web頁(yè)面全鏈路性能優(yōu)化指南

性能優(yōu)化不單指優(yōu)化一個(gè)頁(yè)面的打開(kāi)速度,在開(kāi)發(fā)環(huán)境將一個(gè)項(xiàng)目的啟動(dòng)時(shí)間縮短使開(kāi)發(fā)體驗(yàn)更好也屬于性能優(yōu)化,大文件上傳時(shí)為其添加分片上傳、斷點(diǎn)續(xù)傳也屬于性能優(yōu)化。在項(xiàng)目開(kāi)發(fā)以及用戶使用的過(guò)程中,能夠讓任何一個(gè)鏈路快一點(diǎn),都可以被叫做性能優(yōu)化。
本文會(huì)對(duì)web頁(yè)面的全鏈路進(jìn)行完整的講解并針對(duì)每一步找到能做的性能優(yōu)化點(diǎn),本文的目標(biāo)是極致的性能優(yōu)化。
因?yàn)獒槍?duì)性能優(yōu)化,能做的點(diǎn)會(huì)特別特別的多,覆蓋著整個(gè)互聯(lián)網(wǎng)的訪問(wèn)流程,因此此文章的內(nèi)容會(huì)比較多且雜,筆者會(huì)盡量對(duì)內(nèi)容進(jìn)行分類講解。
本文的大致流程為先講理論知識(shí),比如如何評(píng)價(jià)一個(gè)頁(yè)面的性能好與不好、如果獲取性能指標(biāo),如何使用各種性能相關(guān)工具,瀏覽器如何獲取并渲染頁(yè)面。筆者認(rèn)為這些都是基礎(chǔ),只有了解了這些基礎(chǔ)才能開(kāi)始考慮如何去優(yōu)化。
接下來(lái)我們會(huì)進(jìn)入性能優(yōu)化環(huán)節(jié),在這個(gè)環(huán)節(jié)我會(huì)詳細(xì)講解在頁(yè)面的整個(gè)流程中,哪些地方可以做哪些優(yōu)化。
目錄
進(jìn)程與線程
輸入url到頁(yè)面展示完整過(guò)程
1.用戶輸入
2.卸載原頁(yè)面并重定向到新頁(yè)面
3.處理Service Worker
4.網(wǎng)絡(luò)請(qǐng)求
5.服務(wù)端響應(yīng)
6.瀏覽器渲染詳細(xì)流程
瀏覽器處理每一幀的流程
Chrome Performance(性能)
Chrome Performance 工具的使用
Performance API介紹
使用Performance API獲取性能相關(guān)指標(biāo)
Coverage(覆蓋率)
Lighthouse
Network(網(wǎng)絡(luò))
網(wǎng)絡(luò)請(qǐng)求中的Timing(時(shí)間)
網(wǎng)絡(luò)請(qǐng)求的優(yōu)先級(jí)
網(wǎng)頁(yè)總資源信息
Network配置
網(wǎng)絡(luò)優(yōu)化策略
減少HTTP請(qǐng)求數(shù)
使用HTTP緩存
使用 HTTP/2.0
避免重定向
使用 dns-prefetch
使用域名分片
CDN
壓縮
使用contenthash
合理使用preload、prefetch
瀏覽器渲染優(yōu)化策略
關(guān)鍵渲染路徑
強(qiáng)制同步布局問(wèn)題
如何減少重排與重繪
靜態(tài)文件優(yōu)化策略
圖片格式
圖片優(yōu)化
HTML優(yōu)化
CSS優(yōu)化
JS優(yōu)化
字體優(yōu)化
瀏覽器儲(chǔ)存優(yōu)化策略
Cookie
LocalStorage
SessionStorage
IndexDB
其他優(yōu)化策略
使用PWA提高用戶體驗(yàn)
瀏覽器渲染原理
我們需要知道瀏覽器是如何渲染一個(gè)頁(yè)面的,我們才能知道如何對(duì)頁(yè)面進(jìn)行性能優(yōu)化,所以這里我們對(duì)一些基礎(chǔ)知識(shí)進(jìn)行講解
進(jìn)程與線程
瀏覽器有多種進(jìn)程,其中最主要的5種進(jìn)程如下

瀏覽器進(jìn)程 負(fù)責(zé)界面展示、用戶交互、子進(jìn)程管理、提供存儲(chǔ)等 渲染進(jìn)程 每個(gè)頁(yè)面都有一個(gè)單獨(dú)的渲染進(jìn)程,用于渲染頁(yè)面,包含webworker線程 網(wǎng)絡(luò)進(jìn)程 主要處理網(wǎng)絡(luò)資源加載(HTML、CSS、JS、IMAGE、AJAX等) GPU進(jìn)程 3D繪制,提高性能 插件進(jìn)程 chrome插件,每個(gè)插件占用一個(gè)進(jìn)程
輸入url到頁(yè)面展示完整過(guò)程
圖1

1.用戶輸入
用戶在瀏覽器進(jìn)程輸入并按下回車健后,瀏覽器判斷用戶輸入的url是否為正確的url,如果不是,則使用默認(rèn)的搜索引擎將該關(guān)鍵字拼接成url。
2.卸載原頁(yè)面并重定向到新頁(yè)面
然后瀏覽器會(huì)將現(xiàn)有頁(yè)面卸載掉并重定向到用戶新輸入的url頁(yè)面,也就是圖中【Process Unload Event】和【Redirect】流程。
此時(shí)瀏覽器會(huì)準(zhǔn)備一個(gè)渲染進(jìn)程用于渲染即將到來(lái)的頁(yè)面,和一個(gè)網(wǎng)絡(luò)進(jìn)程用于發(fā)送網(wǎng)絡(luò)請(qǐng)求。
3.處理Service Worker
如果當(dāng)前頁(yè)面注冊(cè)了Service Worker那么它可以攔截當(dāng)前網(wǎng)站所有的請(qǐng)求,進(jìn)行判斷是否需要向遠(yuǎn)程發(fā)送網(wǎng)絡(luò)請(qǐng)求。也就是圖中【Service Worker Init】與【Service Worker Fecth Event 】步驟
如果不需要發(fā)送網(wǎng)絡(luò)請(qǐng)求,則取本地文件。如果需要?jiǎng)t進(jìn)行下一步。
4.網(wǎng)絡(luò)請(qǐng)求
OSI網(wǎng)絡(luò)七層模型:物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會(huì)話層、表示層、應(yīng)用層
在實(shí)際應(yīng)用中物理層、數(shù)據(jù)鏈路層被統(tǒng)稱為物理層,會(huì)話層、表示層、應(yīng)用層被統(tǒng)稱為應(yīng)用層,所以實(shí)際使用時(shí)通常分為4個(gè)層級(jí)
【物理層】>【網(wǎng)絡(luò)層(IP)】>【傳輸層(TCP/UDP)】>【應(yīng)用層(HTTP)】
也就是圖中【HTTP Cache】、【DNS】、【TCP】、【Request】、【Response】步驟
圖2

瀏覽器會(huì)拿著url通過(guò)網(wǎng)絡(luò)進(jìn)程進(jìn)行如下步驟
根據(jù)url查詢本地是否已經(jīng)有強(qiáng)制緩存,如果有則判斷緩存是否過(guò)期,如果沒(méi)過(guò)期則直接返回緩存內(nèi)容,也就是圖1中【HTTP Cache】步驟
如果沒(méi)有強(qiáng)制緩存或者緩存已過(guò)期,則將該請(qǐng)求加入隊(duì)列進(jìn)行排隊(duì)準(zhǔn)備發(fā)送網(wǎng)絡(luò)請(qǐng)求,也就是圖2中【正在排隊(duì)】,然后進(jìn)入DNS解析階段,也就是圖1中【DNS】以及圖2中的【DNS查找】,DNS根據(jù)域名解析出對(duì)應(yīng)的IP地址。(DNS基于UDP)。
然后使用IP尋址找到對(duì)方,然后根據(jù)IP地址+端口號(hào)創(chuàng)建一個(gè)TCP連接(三次握手),也就是圖1中【TCP】以及圖2中的【初始連接】創(chuàng)建完成后利用TCP連接來(lái)傳輸數(shù)據(jù)。(TCP會(huì)將數(shù)據(jù)拆分為多個(gè)數(shù)據(jù)包,進(jìn)行有序傳輸,如果丟包會(huì)重發(fā),TCP的特點(diǎn)是可靠、有序)
判斷當(dāng)前協(xié)議是否為https,如果為https,則進(jìn)行SSL協(xié)商,將數(shù)據(jù)進(jìn)行加密,如果為http協(xié)議則不進(jìn)行加密(明文傳輸),也就是圖2中的【SSL】。
開(kāi)始發(fā)送http請(qǐng)求(請(qǐng)求行/請(qǐng)求頭/請(qǐng)求體),也就是圖1中【Request】以及圖2中的【已發(fā)送請(qǐng)求】。HTTP協(xié)議有多個(gè)版本,目前使用最多的版本為HTTP/1.1,HTTP/1.1發(fā)送完成后默認(rèn)不會(huì)斷開(kāi)。keep-alive 默認(rèn)打開(kāi),為了下次傳輸數(shù)據(jù)時(shí)復(fù)用上次創(chuàng)建的連接。每個(gè)域名最多同時(shí)建立6個(gè)TCP連接,所以同一時(shí)間最多發(fā)生6個(gè)請(qǐng)求。
HTTP協(xié)議的各個(gè)版本特性如下:
HTTP/0.9沒(méi)有請(qǐng)求頭和響應(yīng)頭,不區(qū)分傳輸?shù)膬?nèi)容類型,因?yàn)楫?dāng)時(shí)只傳輸HTML。HTTP/1.0提供了請(qǐng)求頭和響應(yīng)頭,可以傳輸不同類型的內(nèi)容數(shù)據(jù)。根據(jù)請(qǐng)求響應(yīng)頭的不同來(lái)處理不同的資源,HTTP1.0每次發(fā)完請(qǐng)求都會(huì)斷開(kāi)TCP連接。有新的請(qǐng)求時(shí)再次創(chuàng)建TCP連接。HTTP/1.1默認(rèn)開(kāi)啟了 keep-alive ,它能夠讓一個(gè)TCP連接中傳輸多個(gè)HTTP請(qǐng)求,也叫鏈路復(fù)用。但一個(gè)TCP連接同一時(shí)間只能發(fā)送一個(gè)HTTP請(qǐng)求,為了不阻塞多個(gè)請(qǐng)求,Chrome允許創(chuàng)建6個(gè)TCP連接,所以在HTTP/1.1中,最多能夠同時(shí)發(fā)送6個(gè)網(wǎng)絡(luò)請(qǐng)求。HTTP/2.0HTTP/2.0使用同一個(gè)TCP連接來(lái)發(fā)送數(shù)據(jù),他把多個(gè)請(qǐng)求通過(guò)二進(jìn)制分貞層實(shí)現(xiàn)了分貞,然后把數(shù)據(jù)傳輸給服務(wù)器。也叫多路復(fù)用,多個(gè)請(qǐng)求復(fù)用同一個(gè)TCP連接。HTTP/2.0會(huì)將所有以:開(kāi)頭的請(qǐng)求頭做一個(gè)映射表,然后使用hpack進(jìn)行壓縮,使用這種方式會(huì)使請(qǐng)求頭更小。服務(wù)器可主動(dòng)推送數(shù)據(jù)給客戶端。HTTP/3.0使用UDP實(shí)現(xiàn),在UDP上一層加入一層QUIC協(xié)議,解決了TCP協(xié)議中的隊(duì)頭阻塞問(wèn)題。服務(wù)器收到數(shù)據(jù)后解析HTTP請(qǐng)求(請(qǐng)求行/請(qǐng)求頭/請(qǐng)求體),處理完成后生成狀態(tài)碼和HTTP響應(yīng)(響應(yīng)行/響應(yīng)頭/響應(yīng)體)后返回給客戶端,也就是圖2的【等待中】在做的事情。
客戶端接收到HTTP響應(yīng)后根據(jù)狀態(tài)碼進(jìn)行對(duì)應(yīng)的處理,如果狀態(tài)碼為304則直接代表協(xié)商緩存生效,直接取本地的緩存文件。如果不是則下載內(nèi)容。也就是圖1中【Response】以及圖2中的【下載內(nèi)容】步驟。
5.服務(wù)端響應(yīng)
在4.網(wǎng)絡(luò)請(qǐng)求第6步中,服務(wù)器收到HTTP請(qǐng)求后需要根據(jù)請(qǐng)求信息來(lái)進(jìn)行解析,并返回給客戶端想要的數(shù)據(jù),這也就服務(wù)端響應(yīng)。
服務(wù)端可以響應(yīng)并返回給客戶端很多種類型的資源,這里主要介紹html類型
目前前端處理服務(wù)端響應(yīng)html請(qǐng)求主要分為SSR服務(wù)端渲染與CSR客戶端渲染,CSR就是返回一個(gè)空的HTML模版,然后瀏覽器加載js后通過(guò)js動(dòng)態(tài)渲染頁(yè)面。SSR是服務(wù)端在接受到請(qǐng)求時(shí)事先在服務(wù)端渲染好html返回給客戶端后,客戶端再進(jìn)行客戶端激活。
在打開(kāi)一個(gè)站點(diǎn)的首屏頁(yè)的完整鏈路中,使用SSR服務(wù)端渲染時(shí)的速度要遠(yuǎn)大于CSR客戶端渲染,并且SSR對(duì)SEO友好。所以對(duì)于首屏加載速度比較敏感或者需要優(yōu)化SEO的站點(diǎn)來(lái)說(shuō),使用SSR是更好的選擇。
6.瀏覽器渲染詳細(xì)流程
瀏覽器渲染詳細(xì)流程主要在4.網(wǎng)絡(luò)請(qǐng)求中的地7步。瀏覽器下載完html內(nèi)容后進(jìn)行解析何渲染頁(yè)面的流程。

渲染流程分為4種情況,
HTML中無(wú)任何CSS相關(guān)標(biāo)簽 CSS相關(guān)標(biāo)簽在HTML最頂部,且在解析到內(nèi)容標(biāo)簽( )時(shí)已經(jīng)解析完CSS相關(guān)標(biāo)簽CSS相關(guān)標(biāo)簽在HTML最頂部,但在解析到內(nèi)容標(biāo)簽( )時(shí)CSS相關(guān)標(biāo)簽尚未解析完CSS相關(guān)標(biāo)簽在HTML最底部
下面的流程是對(duì)上圖的文字版解析。讀者可將以上4種情況分別帶入到如下的渲染流程中走一遍。就能理解瀏覽器的完整渲染過(guò)程了。
【HTML】
瀏覽器收到html資源后先預(yù)掃描和
