閱讀目錄
DNS 解析成 IP 地址
發(fā)送 http 請(qǐng)求
TCP 傳輸報(bào)文
IP 尋址
封裝成幀
物理傳輸
頁面渲染主流程
dom樹和render樹的關(guān)系
布局render樹(layout)
繪制(paint)
之前面試時(shí)候經(jīng)常被問及這個(gè)問題,支支吾吾回答沒有底氣,仔細(xì)研究了一下,發(fā)現(xiàn)里面學(xué)問還真不少。
從輸入 cnblogs.com 到博客園首頁完全展現(xiàn)這個(gè)過程可以大致分為 網(wǎng)絡(luò)通信 和 頁面渲染 兩個(gè)步驟。
網(wǎng)絡(luò)通信走的五層因特網(wǎng)協(xié)議棧(OSI標(biāo)準(zhǔn)是七層模型,但實(shí)際實(shí)現(xiàn)通常是五層)。畫了一張圖:
五層因特網(wǎng)協(xié)議棧
DNS 解析成 IP 地址
DNS屬于應(yīng)用層協(xié)議??蛻舳藭?huì)先檢查本地是否有對(duì)應(yīng)的 ip 地址,如果有就返回,否則就會(huì)請(qǐng)求上級(jí) DNS 服務(wù)器,知道找到或到根節(jié)點(diǎn)。這一過程可能會(huì)非常耗時(shí),使用 dns-prefetch 可使瀏覽器在空閑時(shí)提前將這些域名轉(zhuǎn)化為 ip 地址,真正請(qǐng)求資源時(shí)就避免了這個(gè)過程的時(shí)間。例如京東首頁的處理:
京東首頁dns-prefetch處理
發(fā)送 http 請(qǐng)求
HTTP也是應(yīng)用層協(xié)議。HTTP(HyperText Transport Protocol)定義了一個(gè)基于請(qǐng)求/響應(yīng)模式的、無狀態(tài)的、應(yīng)用層的協(xié)議,用于從萬維網(wǎng)服務(wù)器傳輸超文本到本地瀏覽器。絕大多數(shù)的Web開發(fā),都是構(gòu)建在HTTP協(xié)議之上的Web應(yīng)用??蛻舳私M織并發(fā)送 http 請(qǐng)求報(bào)文,包含 method、url、host、cookie 等信息,下面是訪問博客園首頁時(shí) http 請(qǐng)求報(bào)文的樣子:
GET https://www.cnblogs.com/ HTTP/1.1
Host: www.cnblogs.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: __gads=ID=b62b1e22b7de2e02:T=1493954370:S=ALNI_MYRebVRavER2PJmwdeFwpl33ACNoQ;
If-Modified-Since: Mon, 27 Nov 2017 12:21:04 GMT
請(qǐng)求頭里的每個(gè)字段都有各自的作用,具體含義可查閱 http 協(xié)議相關(guān)文章。
TCP 傳輸報(bào)文
TCP 將 http 長報(bào)文劃分為短報(bào)文,通過“三次握手”與服務(wù)器建立連接,進(jìn)行可靠傳輸?!叭挝帐帧苯⑦B接的過程和打電話極像:
客戶端:喂,我要和 Server 通話
服務(wù)端:你好,我是 Server,你是 Client 嗎
客戶端:沒錯(cuò),我是 Client
連接建立成功,接下來就可以正式傳送數(shù)據(jù)了。
數(shù)據(jù)傳完之后斷開tcp連接還要通過“四次揮手”,大概意思如下:
客戶端:Server 小寶貝,我話說完了,你掛電話吧
服務(wù)端:我不掛,我不掛,你先掛,你不掛我也不掛
---------------- Client 一陣無語 --------------
服務(wù)端:你掛了嗎
客戶端:行,那我先掛了
至此完成了一次完整的資源請(qǐng)求響應(yīng)。
需要注意的是,瀏覽器對(duì)同一域名下并發(fā)的tcp連接數(shù)是有限制的,2個(gè)到10個(gè)不等。為了解決這個(gè)資源加載瓶頸,有幾種流行的優(yōu)化方案:
資源打包,合并請(qǐng)求
比如頁面樣式全部打包在一個(gè) css 文件內(nèi),頁面邏輯全部打包在一個(gè) js 文件內(nèi),圖片拼合成雪碧圖,這樣可有效減少頁面的資源請(qǐng)求數(shù)量。webpack 是時(shí)下最流行的模塊打包工具之一,它可以將頁面內(nèi)所有資源(包括js,css,圖片,字體等等)都打包進(jìn)一個(gè) js 文件,不明覺厲。
域名拆分,資源分散存儲(chǔ)
當(dāng)瀏覽器向服務(wù)器請(qǐng)求一個(gè)靜態(tài)資源時(shí),會(huì)先發(fā)送該域名下的 cookies,服務(wù)器對(duì)于這些 cookie 根本不會(huì)做任何處理,因此它們只是在毫無意義的消耗帶寬,所以應(yīng)該確保對(duì)于靜態(tài)內(nèi)容的請(qǐng)求是無 cookie 的請(qǐng)求(也就是所謂的 cookie-free)。將站點(diǎn)的 js、css、圖片等靜態(tài)文件放在一個(gè)專門的域名下訪問,由于該域名與主站域名不同,所以瀏覽器就不會(huì)把主域名下的 cookies 傳給該域,從而減少網(wǎng)絡(luò)開銷,特別是細(xì)碎靜態(tài)文件特別多的情況下效果顯著。
另一方面,由于瀏覽器是基于域名的并發(fā)連接數(shù)限制,而不是頁面。因此將資源部署在不同的域名下可以使頁面的總并發(fā)連接數(shù)得到線性提升。
Connection: keep-alive,復(fù)用已建立的連接
在 http 早期,每個(gè) http 請(qǐng)求都要打開一個(gè) tcp 連接,請(qǐng)求完就關(guān)閉這個(gè)連接,導(dǎo)致每個(gè)請(qǐng)求都要來一遍“三次握手”和“四次揮手”,從而磨磨唧唧多出來大量無謂的等待時(shí)間。就好比出去吃飯,等飯等半個(gè)小時(shí),端上來十分鐘吃完了,結(jié)賬排隊(duì)又等了半個(gè)小時(shí),要是剛進(jìn)來就吃現(xiàn)成的吃完就跑那多爽啊。keep-alive 干的就是這件事,當(dāng)?shù)谝粋€(gè)請(qǐng)求數(shù)據(jù)傳輸完畢之后,服務(wù)器說“客戶端你不要關(guān)閉這個(gè)連接,直接換下個(gè)請(qǐng)求,我不想再握你的破手了”。這樣下個(gè)請(qǐng)求就直接傳輸數(shù)據(jù)而不用先走“三次握手”的流程了。這好比你又去吃飯,吃你最喜歡的紅燒肉,飯店在今天第一個(gè)客人點(diǎn)紅燒肉的時(shí)候就炒了一大鍋紅燒肉,你點(diǎn)餐的時(shí)候直接吃現(xiàn)成的就行了,吃完直接跑,哈哈美滋滋。
控制緩存
將靜態(tài)資源強(qiáng)制緩存在客戶端,通過添加文件指紋等方式使客戶端只請(qǐng)求發(fā)生了變更的資源,可有效降低靜態(tài)資源請(qǐng)求數(shù)量。具體可參看前端靜態(tài)資源緩存控制策略。
延遲加載,懶加載,按需加載
很多頁面瀏覽量雖然很大,但其實(shí)很大比例用戶掃完第一屏就直接跳走了,第一屏以下的內(nèi)容用戶根本就不感興趣。 對(duì)于超大流量的網(wǎng)站,這個(gè)問題尤其重要。這時(shí)可根據(jù)用戶的行為進(jìn)行按需加載,用戶用到了就去加載,用不到就不去加載。
以上都是從減少建立tcp連接數(shù)量的角度去優(yōu)化頁面性能,之后會(huì)分享更多前端性能優(yōu)化方面的實(shí)用方法。
IP 尋址
Internet Protocol 是定義網(wǎng)絡(luò)之間彼此互聯(lián)規(guī)則的協(xié)議,主要解決邏輯尋址和網(wǎng)絡(luò)通用數(shù)據(jù)傳輸格式兩個(gè)問題。
所有連接到因特網(wǎng)上的設(shè)備都會(huì)被分配一個(gè)唯一的 IP 地址,就像網(wǎng)購時(shí)填寫的收貨地址一樣。由于一個(gè)網(wǎng)絡(luò)設(shè)備的 IP 地址可以更換,但是 MAC 硬件地址(就像身份證號(hào))一般是固定不變的,所以首先使用 ARP 協(xié)議來找到目標(biāo)主機(jī)的 MAC 硬件地址。當(dāng)通信的雙方不在同一個(gè)局域網(wǎng)時(shí),需要多次中轉(zhuǎn)(路由器)才能找到最終的目標(biāo),在中轉(zhuǎn)的過程中還需要通過下一個(gè)中轉(zhuǎn)站的 MAC 地址來搜索下一個(gè)中轉(zhuǎn)目標(biāo)。
傳輸層傳來的 TCP 報(bào)文會(huì)在這一層被 IP 封裝成網(wǎng)絡(luò)通用傳輸格式——IP數(shù)據(jù)包,IP 數(shù)據(jù)包是真正在網(wǎng)絡(luò)間進(jìn)行傳輸?shù)臄?shù)據(jù)基本單元。
通過邏輯尋址定位到前面應(yīng)用層 DNS 解析出來的 IP 地址的主機(jī)網(wǎng)絡(luò)位置,然后把數(shù)據(jù)以 IP 數(shù)據(jù)包的格式發(fā)送到那去。
封裝成幀
數(shù)據(jù)鏈路層負(fù)責(zé)將 IP 數(shù)據(jù)包封裝成適合在物理網(wǎng)絡(luò)上傳輸?shù)膸袷讲鬏?。設(shè)計(jì)數(shù)據(jù)鏈路層的主要目的就是在原始的、有差錯(cuò)的物理傳輸線路的基礎(chǔ)上,采取差錯(cuò)檢測、差錯(cuò)控制與流量控制等方法,將有差錯(cuò)的物理線路改進(jìn)成邏輯上無差錯(cuò)的數(shù)據(jù)鏈路,向網(wǎng)絡(luò)層提供高質(zhì)量的服務(wù)。當(dāng)采用復(fù)用技術(shù)時(shí),一條物理鏈路上可以有多條數(shù)據(jù)鏈路。
物理傳輸
上面這么多層其實(shí)都是在為不同的目的對(duì)要傳輸?shù)臄?shù)據(jù)進(jìn)行封裝處理,而物理層則是通過各種傳輸介質(zhì)(雙絞線,電磁波,光纖等)以信號(hào)的形式將上面各層封裝好的數(shù)據(jù)物理傳送過去。
至此一個(gè) http 請(qǐng)求漂洋過海終于到達(dá)了服務(wù)器,接下來就是從物理層到應(yīng)用層向上傳遞,將封裝的數(shù)據(jù)一層層剝開,服務(wù)器在應(yīng)用層拿到最原始的請(qǐng)求信息后快速處理完,然后就開始向客戶端發(fā)送響應(yīng)信息。這次是以服務(wù)器為起點(diǎn),客戶端為終點(diǎn)再走一遍五層協(xié)議棧。
服務(wù)器的響應(yīng)消息跋山涉水終于到達(dá)了瀏覽器,接下來就是頁面渲染(更具體可參看瀏覽器內(nèi)部工作原理)。
頁面的渲染工作主要由瀏覽器的渲染引擎來完成(這里以Chrome為例)。
頁面渲染主流程
下面是渲染引擎在取得內(nèi)容后的基本流程:
解析html構(gòu)建dom樹 -> 解析css構(gòu)建render樹 -> 布局render樹 -> 繪制render樹
渲染引擎首先開始解析html,并將標(biāo)簽轉(zhuǎn)化為dom樹中的dom節(jié)點(diǎn)。接著,它解析外部css文件及style標(biāo)簽中的樣式信息,這些樣式信息以及html標(biāo)簽中的可見性指令將被用來構(gòu)建另一棵樹——render樹。render樹構(gòu)建好了之后,將會(huì)執(zhí)行布局過程,該過程將確定render樹每個(gè)節(jié)點(diǎn)在屏幕上的確切坐標(biāo)。最后是繪制render樹,即遍歷render樹的每個(gè)節(jié)點(diǎn)并將它們繪制到屏幕上。
偷了一張圖片(Chrome和Safari所用內(nèi)核webkit頁面渲染主流程):
webkit頁面渲染主流程
為了更好的用戶體驗(yàn),渲染引擎將會(huì)盡可能早地將內(nèi)容繪制在屏幕上,而不會(huì)等到所有的html都解析完成后再去構(gòu)建、布局和繪制render樹,它是解析完一部分內(nèi)容就繪制一部分內(nèi)容,同時(shí)可能還在通過網(wǎng)絡(luò)下載其余內(nèi)容(圖片,腳本,樣式表等)。比如說,瀏覽器在代碼中發(fā)現(xiàn)一個(gè) img 標(biāo)簽引用了一張圖片,于是就向服務(wù)器發(fā)出圖片請(qǐng)求,此時(shí)瀏覽器不會(huì)等到圖片下載完,而是會(huì)繼續(xù)解析渲染后面的代碼,等到服務(wù)器返回圖片文件,由于圖片占用了一定面積,影響了后面段落的布局,瀏覽器就會(huì)回過頭來重新渲染這部分代碼。
dom樹和render樹的關(guān)系
render樹節(jié)點(diǎn)和dom樹節(jié)點(diǎn)相對(duì)應(yīng),但這種對(duì)應(yīng)關(guān)系不是一對(duì)一的,不可見的dom元素不會(huì)被插入render樹,例如head元素、script元素等。另外,display屬性為none的元素也不會(huì)在渲染樹中出現(xiàn)(visibility屬性為hidden的元素將出現(xiàn)在渲染樹中,這是因?yàn)関isibility屬性為hidden的元素雖然不可見但保留了元素的占位)。
聯(lián)系客服