前言
從打開(kāi)瀏覽器輸入網(wǎng)址到最終的網(wǎng)頁(yè)呈現(xiàn)在瀏覽器中慈参,到底經(jīng)歷了哪些過(guò)程推沸?下面為大家一一講解。
總體分為以下幾個(gè)過(guò)程:
- DNS解析——解析域名议泵,獲取對(duì)應(yīng)的ip地址
- TCP連接——TCP三次握手
- 瀏覽器發(fā)送http請(qǐng)求
- 服務(wù)器處理請(qǐng)求并返回http報(bào)文
- 瀏覽器解析返回的數(shù)據(jù)并渲染頁(yè)面
- 斷開(kāi)連接:TCP四次揮手
正文
DNS解析
什么是URL
在講解DNS解析前占贫,先簡(jiǎn)單介紹下URL。所謂的URL(Uniform Resource Locator)先口,中文名統(tǒng)一資源定位符型奥,用于定位互聯(lián)網(wǎng)上的資源。簡(jiǎn)單來(lái)說(shuō)碉京,就是人們常說(shuō)的網(wǎng)址厢汹。
一個(gè)標(biāo)準(zhǔn)的URL格式遵循一定的語(yǔ)法規(guī)則:
scheme://host.domain.port/path/filename
對(duì)應(yīng)部分解釋如下:
- scheme——定義因特網(wǎng)服務(wù)類(lèi)型。比如http谐宙,ftp烫葬,file,https
- host——定義域主機(jī)(http默認(rèn)的域主機(jī)是www)
- domain——定義了域名凡蜻,比如:baidu.com
- port——定義主機(jī)上的端口號(hào)(http默認(rèn)端口80)
- path——定義資源在主機(jī)上的路徑
- filename——定義資源文件名
好啦搭综,當(dāng)我們?cè)跒g覽器中輸入一個(gè)網(wǎng)址的時(shí)候,瀏覽器就會(huì)對(duì)該網(wǎng)址進(jìn)行DNS解析還獲取其對(duì)應(yīng)的ip地址划栓,而這個(gè)ip地址兑巾,才是資源實(shí)際存在的地址。
什么是ip地址
ip地址忠荞,指的是互聯(lián)網(wǎng)協(xié)議地址蒋歌。每一個(gè)互聯(lián)網(wǎng)上的主機(jī)都會(huì)分配有一個(gè)ip地址來(lái)作為其身份的標(biāo)識(shí)帅掘。ip地址是一個(gè)32位的二進(jìn)制數(shù),比如本機(jī)的ip地址為127.0.0.1奋姿。而域名相當(dāng)于是對(duì)ip地址的一個(gè)偽裝锄开。因?yàn)橄鄬?duì)于純數(shù)字的ip地址來(lái)說(shuō),具有一定語(yǔ)義的域名更容易被人所理解和記住称诗。而計(jì)算機(jī)更喜歡純數(shù)字的ip地址萍悴,所以為了同時(shí)滿(mǎn)足以上兩者的需求,DNS服務(wù)應(yīng)運(yùn)而生寓免。
什么是域名解析
DNS協(xié)議童工通過(guò)域名查找ip地址或是逆向地從ip地址查找域名的服務(wù)癣诱。DNS是一個(gè)網(wǎng)絡(luò)服務(wù)器,簡(jiǎn)單來(lái)說(shuō)上面存放了各個(gè)域名和其ip地址之間的關(guān)系數(shù)據(jù)袜香。
比如:baidu.com 220.114.23.56
域名解析過(guò)程
- 瀏覽器緩存——瀏覽器會(huì)按照一定的頻率緩存DNS記錄
- 操作系統(tǒng)緩存——如果瀏覽器緩存中沒(méi)有記錄撕予,會(huì)到本地操作系統(tǒng)緩存中查詢(xún)。(hosts文件)
- 路由緩存——路由器也有DNS緩存
- ISP的DNS服務(wù)器:ISP(Internet Service Provider)蜈首,是互聯(lián)網(wǎng)服務(wù)提供商的簡(jiǎn)稱(chēng)实抡。ISP有專(zhuān)門(mén)的DNS服務(wù)器對(duì)應(yīng)DNS查詢(xún)請(qǐng)求。
- 根服務(wù)器:ISP的DNS服務(wù)器如果還找不到的話欢策,ISP會(huì)代替用戶(hù)想根服務(wù)器發(fā)起查詢(xún)請(qǐng)求吆寨,進(jìn)行查詢(xún)。根域名服務(wù)器返回.com域名服務(wù)器的ip地址踩寇,然后訪問(wèn).com域名服務(wù)器獲取baidu.com域名服務(wù)器的ip地址啄清,最后在訪問(wèn)baidu.com域名服務(wù)器獲取www.baidu.com的ip地址,至此DNS域名解析完成俺孙。
TCP三次握手
TCP辣卒,一種傳輸控制協(xié)議,是一種面向連接的睛榄、可靠的荣茫、基于字節(jié)流的傳輸層通信協(xié)議。
網(wǎng)絡(luò)七層模型
應(yīng)用層(ftp场靴,http计露,smtp,pop3)——表示層——會(huì)話層——傳輸層(TCP)——網(wǎng)絡(luò)層(IP)——數(shù)據(jù)鏈路層——物理層
TCP三次握手
- 客戶(hù)端發(fā)送有待帶有SYN=1憎乙,Seq=x的syn包到服務(wù)器票罐,并進(jìn)入到SYN_SEND狀態(tài),等待服務(wù)器確認(rèn)泞边。
- 服務(wù)器收到syn包后该押,必須確認(rèn)客戶(hù)的SYN(ack=x+1),同時(shí)發(fā)送一個(gè)自己的SYN(seq=k)包給客戶(hù)端(即SYN+ACK包)阵谚,同時(shí)服務(wù)器進(jìn)入到SYN_RECV狀態(tài)蚕礼。
- 客戶(hù)端收到服務(wù)器的SYN+ACK包后烟具,向服務(wù)器發(fā)一個(gè)確認(rèn)包ACK(k+1),這些包發(fā)送完畢后奠蹬,客戶(hù)端和服務(wù)器同時(shí)進(jìn)入到ESTABLISHED狀態(tài)朝聋,完成三次握手。
注:tcp在握手過(guò)程中并不攜帶任何數(shù)據(jù)囤躁,而是在三次握手完成之后冀痕,才會(huì)進(jìn)行數(shù)據(jù)的傳遞。
為什么需要三次握手狸演?
《計(jì)算機(jī)網(wǎng)絡(luò)》中講“三次握手”的目的是“為了防止已經(jīng)失效的連接請(qǐng)求報(bào)文突然又傳送到了服務(wù)端言蛇,因而產(chǎn)生了錯(cuò)誤”。
可以這樣理解宵距,客戶(hù)端發(fā)起了一個(gè)連接請(qǐng)求腊尚,但是因?yàn)橐恍┰蛘?qǐng)求并未及時(shí)傳遞到服務(wù)器。而客戶(hù)端因?yàn)殚L(zhǎng)時(shí)間收不到服務(wù)器的就認(rèn)為本地連接請(qǐng)求失敗满哪,然后就去做別的事了婿斥。而過(guò)了一段時(shí)間,之前的請(qǐng)求到達(dá)了服務(wù)器哨鸭,服務(wù)器收到請(qǐng)求進(jìn)行一系列操作后返回給客戶(hù)端并等待客戶(hù)端的響應(yīng)受扳。而此時(shí)客戶(hù)端已經(jīng)去做別的事情了,根本不會(huì)對(duì)本次響應(yīng)做出回答兔跌,而服務(wù)器那邊就一直等啊等,等啊等.......這樣就造成了服務(wù)器端的資源浪費(fèi)峡蟋,所以我們需要三次而不是兩次坟桅。這樣服務(wù)器如果在一定時(shí)間內(nèi)沒(méi)收到客戶(hù)端的回答,那么就放棄等待蕊蝗,自己也去干別的事了仅乓。
那為什么不是四次而是三次呢?
因?yàn)榉?wù)器的ACK和SYN包可以一起發(fā)送啊蓬戚,并不會(huì)產(chǎn)生什么不好的影響夸楣,何樂(lè)而不為呢?
發(fā)送http請(qǐng)求
在TCP三次握手結(jié)束后子漩,客戶(hù)端就可以向服務(wù)端發(fā)送http請(qǐng)求報(bào)文了豫喧。
請(qǐng)求報(bào)文包含三部分:請(qǐng)求行,請(qǐng)求頭幢泼,請(qǐng)求主體紧显。
- 請(qǐng)求行包含請(qǐng)求方法,URL缕棵,協(xié)議版本
例:POST /chapter17/user.html HTTP/1.1
8中請(qǐng)求方法:get孵班,post涉兽,put,delete篙程,patch枷畏,head,options虱饿,trace - 請(qǐng)求頭包含了請(qǐng)求的附加信息拥诡,一般以key:value的形式存在。比如關(guān)于客戶(hù)端的信息郭厌,host(主機(jī)名)袋倔。
- 請(qǐng)求體包含了多個(gè)請(qǐng)求參數(shù)的數(shù)據(jù),包含了回車(chē)符折柠、換行符宾娜、請(qǐng)求數(shù)據(jù),并不是所有的請(qǐng)求都帶有請(qǐng)求數(shù)據(jù)扇售。
例:name=tom&age=23 (這里承載著name前塔,age三個(gè)請(qǐng)求參數(shù))
服務(wù)器收到請(qǐng)求后處理請(qǐng)求,并返回響應(yīng)報(bào)文數(shù)據(jù)承冰。
響應(yīng)報(bào)文包含三部分:響應(yīng)行华弓,響應(yīng)頭,響應(yīng)主題困乒。
- 響應(yīng)行包含了協(xié)議版本寂屏,狀態(tài)碼,狀態(tài)碼描述
- 響應(yīng)頭包含了一些附加的響應(yīng)信息
- 相應(yīng)主體包含回車(chē)符娜搂、換行符和響應(yīng)返回的數(shù)據(jù)迁霎,并不是所有的響應(yīng)都有響應(yīng)數(shù)據(jù)
瀏覽器解析并渲染頁(yè)面
瀏覽器在接收到服務(wù)器返回的HTML文件后,會(huì)對(duì)其進(jìn)行HTML解析百宇。
HTML解析是瀏覽器的HTML解析器把HTML解析成DOM TREE考廉。在解析否過(guò)程中,瀏覽器會(huì)根據(jù)HTML文件的結(jié)構(gòu)從上到下解析HTML携御,HTML元素以深度優(yōu)先的方式進(jìn)行解析昌粤,而script、link啄刹、style等標(biāo)簽會(huì)是解析過(guò)程產(chǎn)生阻塞涮坐,阻塞的情況有:
- 外部樣式會(huì)阻塞內(nèi)部腳本的執(zhí)行
- 外部樣式和外部腳本可以并行加載,但是外部樣式會(huì)阻塞外部腳本執(zhí)行
- 如果外部腳本帶有async屬性誓军,則外部腳本的加載與執(zhí)行不受外部樣式影響
- 如果link標(biāo)簽是動(dòng)態(tài)創(chuàng)建生成膊升,不管有無(wú)async屬性谭企,都不會(huì)阻塞外部腳本的加載和執(zhí)行廓译。
在DOM樹(shù)和CSS規(guī)則樹(shù)解析完成生成渲染樹(shù)后评肆,瀏覽器會(huì)進(jìn)入繪制階段。調(diào)用瀏覽器的呈現(xiàn)器的“paint”方法非区,將內(nèi)容呈現(xiàn)在屏幕上瓜挽。
回流:當(dāng)某個(gè)元素的尺寸大小或是位置信息發(fā)生改變的時(shí)候,會(huì)觸發(fā)回流征绸,對(duì)元素的大小和位置進(jìn)行重新計(jì)算久橙。
重繪:當(dāng)某個(gè)元素的背景顏色,文字顏色或是其他不影響周?chē)騼?nèi)部布局的屬性發(fā)生改變時(shí)會(huì)觸發(fā)重繪管怠。
注:回流一定會(huì)包含著重繪淆衷,而重繪不一定會(huì)包含回流。
(插樓)
在實(shí)際情況中渤弛,DOM操作的代價(jià)是非常高的祝拯,而頁(yè)面渲染的瓶頸也都集成中DOM操作上。其中她肯,回流和重繪在DOM操作過(guò)程中是對(duì)性能影響最大的佳头。所以,我們應(yīng)該盡可能的避免不必要的回流和重繪操作晴氨。
會(huì)觸發(fā)回流和重繪的DOM操作:
- 增加康嘉、刪除和修改可見(jiàn)DOM元素
- 頁(yè)面初始化渲染
- 移動(dòng)DOM元素
- 修改CSS樣式,改變DOM元素的尺寸
- DOM元素的內(nèi)容改變籽前,使得尺寸被撐大
- 瀏覽器窗口尺寸改變
- 瀏覽器窗口滾動(dòng)
可以看出亭珍,以上操作是DOM中比較常見(jiàn)的。現(xiàn)代瀏覽器會(huì)針對(duì)重排和重繪做性能優(yōu)化枝哄,如把DOM操作積累一批后統(tǒng)一做一次重排或重繪肄梨。但是在有些情況下,瀏覽器會(huì)立刻重排或重繪膘格。例如:請(qǐng)求下面的DOM元素布局信息:offsetTop/Left/Width/Height、scrollTop\Left\Width\Height财松、clientTop\Left\Width\Height瘪贱、getComputedStyle()或currentStyle。因?yàn)檫@些值都是動(dòng)態(tài)計(jì)算的辆毡,所以瀏覽器需要盡快完成頁(yè)面繪制菜秦,然后計(jì)算返回值,從而打亂了重排或重繪的優(yōu)化舶掖。
所以針對(duì)DOM的優(yōu)化球昨,可以遵循以下幾條實(shí)踐方法:
合并多次的DOM操作為單詞的DOM操作
比如對(duì)DOM元素的多個(gè)css屬性進(jìn)行修改的時(shí)候,可以預(yù)先設(shè)定好css類(lèi)眨攘,然后通過(guò)改變樣式名統(tǒng)一修改DOM樣式主慰。把DOM元素隱藏或離線后修改
把DOM元素從頁(yè)面流中脫離或隱藏嚣州,然后在修改,這樣不會(huì)觸發(fā)重排或重繪共螺。這種方式適合大批量地修改DOM元素该肴,具體有一下3中方式:
(1)使用文檔片段
文檔片段是一個(gè)輕量級(jí)的document對(duì)象,并不會(huì)和特定的頁(yè)面關(guān)聯(lián)藐不。通過(guò)在文檔片段上進(jìn)行DOM操作匀哄,可以降低DOM操作對(duì)頁(yè)面性能的影響。這種方式是創(chuàng)建一個(gè)文檔片段雏蛮,并在此片段上進(jìn)行必要的DOM操作涎嚼,操作完成后將它附加在頁(yè)面中。
var fragment = document.createDocumentFragment ();
//大量基于fragment的DOM操作
//.......
document.getElementById('app').appendChild(fragment);
(2)設(shè)置DOM元素的display樣式為none來(lái)隱藏元素
這種方式通過(guò)隱藏頁(yè)面的DOM元素挑秉,達(dá)到在頁(yè)面中移除元素的效果法梯,經(jīng)過(guò)大量的DOM操作后恢復(fù)原來(lái)的display樣式。只有在一開(kāi)始隱藏和最后顯示的時(shí)候會(huì)觸發(fā)重排和重繪衷模。
var ele = document.getElementById('app');
ele.style.display = 'none';
//大量基于fragment的DOM操作
//.......
ele.style.display = 'block';
(3)克隆DOM元素到內(nèi)存中
這種方式把頁(yè)面中的DOM元素克隆一份到內(nèi)存中鹊汛,然后在內(nèi)存中對(duì)該對(duì)象進(jìn)行操作,完成后再替換頁(yè)面中的元素阱冶。這一也只有在最后一步才會(huì)影響頁(yè)面刁憋。
var old = document.getElementById('app');
var clone = old.cloneNode(true);
//大量基于fragment的DOM操作
//.......
old.parentNode.replaceChild(clone, old);
設(shè)置具有動(dòng)畫(huà)效果的DOM元素的position屬性為fixed或absolute
把頁(yè)面找那個(gè)具有動(dòng)畫(huà)效果的元素設(shè)置為絕對(duì)定位,使得元素脫離頁(yè)面布局流木蹬。從而避免了頁(yè)面繁瑣的重排至耻,只涉及到動(dòng)畫(huà)元素自身的重排。這種做法可以提高動(dòng)畫(huà)效果的展示性能镊叁。如果把動(dòng)畫(huà)元素設(shè)置為絕對(duì)定位并不符合涉及的要求尘颓,則可以在動(dòng)畫(huà)開(kāi)始時(shí)將其設(shè)置為絕對(duì)定位,等動(dòng)畫(huà)結(jié)束后再回復(fù)原來(lái)的設(shè)置晦譬。謹(jǐn)慎取得DOM元素的布局信息
前面說(shuō)過(guò)疤苹,獲取DOM的布局信息會(huì)有性能的損耗,如果重復(fù)的調(diào)用敛腌,最佳的做法是將這些值緩存在局部變量中卧土。使用時(shí)間托管方式綁定事件
在DOM元素上綁定事件會(huì)影響頁(yè)面性能,一方面像樊,綁定事件本身會(huì)占用處理時(shí)間尤莺,另一方面,瀏覽器保存時(shí)間綁定生棍,綁定事件也會(huì)占用內(nèi)存颤霎。頁(yè)面中元素綁定的事件越多,占用的處理時(shí)間和內(nèi)存越大,性能也就相對(duì)越差友酱,因此晴音,在頁(yè)面中綁定事件越少越好。以?xún)?yōu)雅的處理方式是使用時(shí)間托管方式粹污,即利用時(shí)間冒泡機(jī)制段多,只在父元素上綁定事件處理,用于處理所有子元素的事件壮吩,在事件處理函數(shù)中傳入?yún)?shù)判斷時(shí)間源元素进苍,針對(duì)不同的源元素做不同處理刹碾。
document.getElementById('list').addEventListener('click', (e) => {
if(e.target && e.target.nodeName.toUpperCase == 'LI') {
//針對(duì)子元素的處理
//......
}
})
斷開(kāi)連接——四次揮手
在數(shù)據(jù)傳輸完畢后只盹,需要斷開(kāi)tcp連接,此時(shí)會(huì)發(fā)起tcp四次揮手鲫骗。
- 瀏覽器向服務(wù)器發(fā)送報(bào)文(Fin=1沈贝,Ack=z杠人,Seq=x),表示客戶(hù)端請(qǐng)求報(bào)文已經(jīng)發(fā)送完了宋下,準(zhǔn)備關(guān)閉了嗡善。并進(jìn)入到FIN_WAIT_1狀態(tài)。
- 服務(wù)器收到客戶(hù)端的斷開(kāi)請(qǐng)求后学歧,發(fā)送確認(rèn)報(bào)文(Ack=x+1罩引,Seq=z),表示統(tǒng)一關(guān)閉枝笨。此時(shí)主機(jī)進(jìn)入FIN_WAIT_2狀態(tài)袁铐。
- 服務(wù)器在發(fā)送完數(shù)據(jù)以后,也會(huì)向客戶(hù)端發(fā)送斷開(kāi)連接的報(bào)文(Fin=1横浑,Ack=x剔桨,Seq=y),表示我沒(méi)有響應(yīng)數(shù)據(jù)要傳了徙融,準(zhǔn)備關(guān)閉了洒缀。此時(shí)進(jìn)入到LAST_ACK狀態(tài)
- 客戶(hù)端收到服務(wù)器的關(guān)閉請(qǐng)求后,會(huì)發(fā)送一個(gè)確認(rèn)報(bào)文(Ack=y+1欺冀,Seq=x)树绩,表示同意關(guān)閉。服務(wù)器收到客戶(hù)單的確認(rèn)報(bào)文后關(guān)閉連接脚猾。而瀏覽器在等待一段時(shí)間后未收到回復(fù)葱峡,則正常關(guān)閉砚哗。