~~~~~如有錯(cuò)誤,歡迎吐槽~~~~~~
前端開(kāi)發(fā)中我們常常要考慮首屏加載時(shí)間,為了盡可能減少首屏加載時(shí)間我們需要弄清楚從輸入網(wǎng)址到頁(yè)面最終呈現(xiàn)的過(guò)程都發(fā)生了什么事情(感覺(jué)很神秘呀?韬肌!)然后才能具體問(wèn)題具體分析受啥,最終達(dá)到提升網(wǎng)頁(yè)性能的目的做个。
下面我們揭秘這層神秘的面紗,從輸入網(wǎng)址到頁(yè)面呈現(xiàn)大致可分為以下流程:
1.網(wǎng)絡(luò)通信
? ? (1).用戶在地址欄輸入網(wǎng)址滚局,瀏覽器進(jìn)行地址解析居暖。
? ? (2).應(yīng)用層將解析出的域名進(jìn)行域名解析。
? ? (3).傳輸層進(jìn)行tcp三次握手藤肢,建立tcp連接太闺。
? ? (4).應(yīng)用層客戶端向web服務(wù)器發(fā)送HTTP請(qǐng)求。
? ? (5).網(wǎng)絡(luò)層IP協(xié)議查詢MAC地址
? ? (6).服務(wù)器收到處理請(qǐng)求嘁圈。
? ? (7).服務(wù)器發(fā)送HTTP響應(yīng)報(bào)文省骂,瀏覽器收到服務(wù)器響應(yīng),得到html代碼
2.頁(yè)面渲染
? ??(1).解析HTML
????(2).構(gòu)建DOM樹(shù)
????(3).DOM樹(shù)與CSS樣式進(jìn)行附著構(gòu)造呈現(xiàn)(render)樹(shù)
????(4).布局
????(5).繪制
看完上面的流程最住,寶寶還是一臉懵逼呀-><-?別怕钞澳,接下來(lái)小波為大家嘻嘻介紹~~~~
網(wǎng)絡(luò)通信
1.用戶在地址欄輸入網(wǎng)址,瀏覽器進(jìn)行地址解析涨缚。
我們以下面這個(gè)URL為例子轧粟,介紹下普通URL的各部分組成http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
(1).協(xié)議部分:該URL的協(xié)議部分為“http:”,這代表網(wǎng)頁(yè)使用的是HTTP協(xié)議脓魏。在Internet中可以使用多種協(xié)議兰吟,如HTTP,F(xiàn)TP等等本例中使用的是HTTP協(xié)議茂翔。在"HTTP"后面的“//”為分隔符混蔼。
(2).域名部分:該URL的域名部分為“www.aspxfans.com”。一個(gè)URL中檩电,也可以使用IP地址作為域名使用拄丰。
(3).端口部分:跟在域名后面的是端口府树,域名和端口之間使用“:”作為分隔符。端口不是一個(gè)URL必須的部分料按,如果省略端口部分奄侠,將采用默認(rèn)端口。
(4).虛擬目錄部分:從域名后的第一個(gè)“/”開(kāi)始到最后一個(gè)“/”為止载矿,是虛擬目錄部分垄潮。虛擬目錄也不是一個(gè)URL必須的部分。本例中的虛擬目錄是“/news/”闷盔。
(5).文件名部分:從域名后的最后一個(gè)“/”開(kāi)始到“弯洗?”為止,是文件名部分逢勾,如果沒(méi)有“?”,則是從域名后的最后一個(gè)“/”開(kāi)始到“#”為止牡整,是文件部分,如果沒(méi)有“溺拱?”和“#”逃贝,那么從域名后的最后一個(gè)“/”開(kāi)始到結(jié)束,都是文件名部分迫摔。本例中的文件名是“index.asp”沐扳。文件名部分也不是一個(gè)URL必須的部分,如果省略該部分句占,則使用默認(rèn)的文件名沪摄。
(6).錨部分:從“#”開(kāi)始到最后,都是錨部分纱烘。本例中的錨部分是“name”杨拐。錨部分也不是一個(gè)URL必須的部分。
(7).參數(shù)部分:從“凹炸?”開(kāi)始到“#”為止之間的部分為參數(shù)部分戏阅,又稱搜索部分、查詢部分啤它。本例中的參數(shù)部分為“boardID=5&ID=24618&page=1”奕筐。參數(shù)可以允許有多個(gè)參數(shù),參數(shù)與參數(shù)之間用“&”作為分隔符变骡。
URL解析完成后我們已經(jīng)手握域名离赫,接下來(lái)進(jìn)行域名解析。
2.將解析出的域名進(jìn)行域名解析塌碌。
~~~《小插曲:小仙女什么是域名解析呀渊胸??就是域名到IP地址的轉(zhuǎn)換過(guò)程台妆,IP地址是網(wǎng)路上標(biāo)識(shí)你站點(diǎn)的數(shù)字地址翎猛,為了簡(jiǎn)單好記胖翰,采用域名來(lái)替代IP地址。域名的解析工作由DNS服務(wù)器完成切厘。DNS協(xié)議提供通過(guò)域名查找IP地址萨咳,或逆向從IP地址反查域名的服務(wù)》~~~
域名解析過(guò)程
(1).查找瀏覽器緩存,因?yàn)闉g覽器緩存會(huì)記錄DNS記錄一段時(shí)間疫稿,?有趣的是培他,操作系統(tǒng)沒(méi)有告訴瀏覽器儲(chǔ)存DNS記錄的時(shí)間,這樣不同瀏覽器會(huì)儲(chǔ)存?zhèn)€自固定的一個(gè)時(shí)間(2分鐘到30分鐘不等)遗座。
注:瀏覽器的DNS緩存查看和清除
a. chrome中
查看:網(wǎng)址欄輸入:chrome://net-internals/#dns 或 輸入chrome://chrome-urls/就可看到chrome所有的配置界面舀凛,然后選擇chrome://dns或者chrome://net-internals/#dns之后再點(diǎn)擊DNS就可查看。
清除:在chrome://net-internals/#dns頁(yè)面中途蒋,點(diǎn)擊“Clear host cache”猛遍,然后選擇“clear cache”和“flush socket”,可以清空chrome的dns緩存碎绎。
(2).查詢操作系統(tǒng)緩存
(3).hosts查詢
windows系統(tǒng)在C:\Windows\Syatem32\driver\etc\hosts文件中查找螃壤;—Linux系統(tǒng)從/etc/hosts文件中查找抗果。
(4).請(qǐng)求本地域名服務(wù)器(可以認(rèn)為是你的網(wǎng)絡(luò)接入服務(wù)器商提供筋帖,比如中國(guó)電信,中國(guó)移動(dòng)冤馏,阿里云等域名供應(yīng)商)日麸,如果該服務(wù)器有緩存,則直接返回逮光,若沒(méi)有代箭,則下一步。涕刚。嗡综。一般80%到這里就可以了(比如你申請(qǐng)一個(gè)域名,去阿里云杜漠,那么你肯定會(huì)寫上域名所指向的IP凹啊)
(5).從頂級(jí)域名中開(kāi)始查找:根據(jù)本地DNS服務(wù)器的設(shè)置(是否設(shè)置轉(zhuǎn)發(fā)器)進(jìn)行查詢,如果未用轉(zhuǎn)發(fā)模式驾茴,本地DNS就把請(qǐng)求發(fā)至13臺(tái)根DNS盼樟,根DNS服務(wù)器收到請(qǐng)求后會(huì)判斷這個(gè)域名(.com)是誰(shuí)來(lái)授權(quán)管理,并會(huì)返回一個(gè)負(fù)責(zé)該頂級(jí)域名服務(wù)器的一個(gè)IP锈至。本地DNS服務(wù)器收到IP信息后晨缴,將會(huì)聯(lián)系負(fù)責(zé).com域的這臺(tái)服務(wù)器。這臺(tái)負(fù)責(zé).com域的服務(wù)器收到請(qǐng)求后峡捡,如果自己無(wú)法解析击碗,它就會(huì)找一個(gè)管理.com域的下一級(jí)DNS服務(wù)器地址(baidu.com)給本地DNS服務(wù)器筑悴。當(dāng)本地DNS服務(wù)器收到這個(gè)地址后,就會(huì)找baidu.com域服務(wù)器稍途,重復(fù)上面的動(dòng)作雷猪,進(jìn)行查詢,直至找到www.baidu.com主機(jī)晰房。
~~~小可愛(ài)們注意啦:上面4步中按照順序求摇,任何一步查找成功,后面步驟步不用再經(jīng)歷啦
科普時(shí)間到啦J庹摺与境!快過(guò)來(lái)吃瓜~~~什么是dns劫持?猖吴?何可攻擊根域名服務(wù)器的節(jié)點(diǎn)摔刁,發(fā)生在上面第四步,從DNS緩存數(shù)據(jù)庫(kù)中找到時(shí)被惡意改為其他網(wǎng)址海蔽,所以就請(qǐng)求到了其他的網(wǎng)址共屈。
域名解析后我們的法寶又升級(jí)為IP地址了,通過(guò)協(xié)議可以獲得端口(HTTP:80党窜,HTTP:443),下一步該進(jìn)行瀏覽器與服務(wù)器之間的鏈接拗引。
3.進(jìn)行tcp三次握手,建立tcp連接(傳輸層)
位于傳輸層的TCP協(xié)議為傳輸報(bào)文提供可靠的字節(jié)流服務(wù)幌衣。為了方便傳輸矾削,將大塊的數(shù)據(jù)分割成以報(bào)文段為單位的數(shù)據(jù)包進(jìn)行管理,并為它們編號(hào)豁护,方便服務(wù)器接收時(shí)能準(zhǔn)確地還原報(bào)文信息哼凯。TCP協(xié)議通過(guò)“三次握手”等方法保證傳輸?shù)陌踩煽俊?/p>
TCP建立連接過(guò)程(三次握手):
客戶端發(fā)送一個(gè)TCP包。設(shè)置SYN=1(請(qǐng)求建立連接)楚里、Seq=X(隨機(jī)產(chǎn)生的序列號(hào))
服務(wù)器發(fā)回確認(rèn)包(ACK)應(yīng)答断部。SYN=1、ACK=1班缎、ACK number = X+1蝴光、Seq = Y(隨機(jī)產(chǎn)生)
客戶端再次發(fā)送確認(rèn)包(ACK) 。SYN=0吝梅、ACK=1虱疏、ACK number= Y+1、Seq = X+1
TCP斷開(kāi)連接過(guò)程(四次揮手):
客戶機(jī)給服務(wù)器一個(gè)FIN為1的TCP報(bào)文
服務(wù)器返回給客戶端一個(gè)確認(rèn)ACK報(bào)文
服務(wù)器給客戶端發(fā)送一個(gè)FIN報(bào)文
客戶機(jī)回復(fù)ACK報(bào)文
4.客戶端向web服務(wù)器發(fā)送HTTP請(qǐng)求(應(yīng)用層)
HTTP請(qǐng)求消息結(jié)構(gòu)
客戶端發(fā)送一個(gè)HTTP請(qǐng)求到服務(wù)器的請(qǐng)求消息包括以下格式:請(qǐng)求行(request line)苏携,請(qǐng)求頭部(header)做瞪,空行和請(qǐng)求數(shù)據(jù)四部分組成。
第一部分:請(qǐng)求行,用來(lái)說(shuō)明請(qǐng)求類型装蓬,要訪問(wèn)的資源以及所使用的HTTP版本著拭。
第二部分:請(qǐng)求頭部,緊接著請(qǐng)求行之后的部分牍帚,用來(lái)說(shuō)明服務(wù)器要使用的附加信息儡遮,包含很多有關(guān)客戶端環(huán)境和請(qǐng)求正文的有用信息。例如:請(qǐng)求頭可以聲明瀏覽器所用的語(yǔ)言暗赶,請(qǐng)求正文的長(zhǎng)度鄙币。
第三部分:空行,請(qǐng)求頭部后面的空行是必須的蹂随,即使第四部分的請(qǐng)求數(shù)據(jù)為空十嘿,也必須有空行。
第四部分:請(qǐng)求數(shù)據(jù)也叫主體岳锁,可以添加任意的其他數(shù)據(jù)
HTTP響應(yīng)消息結(jié)構(gòu)
HTTP響應(yīng)也由四部分組成:狀態(tài)行绩衷,消息報(bào)頭,空行激率,響應(yīng)正文
第一部分:狀態(tài)行咳燕,右HTTP協(xié)議版本號(hào),狀態(tài)碼乒躺,狀態(tài)消息三部分組成
第二部分:消息報(bào)頭招盲,用來(lái)說(shuō)明客戶端要使用的一些附加信息。
第三部分:空行聪蘸,消息報(bào)頭后面的空行是必須的宪肖。
第四部分:響應(yīng)正文,服務(wù)器返回給客戶端的文本信息健爬。空行后面的html部分為響應(yīng)正文么介。
5.網(wǎng)絡(luò)層IP協(xié)議查詢MAC地址
IP協(xié)議的作用是把TCP分割好的各種數(shù)據(jù)包傳送給接收方娜遵。而要保證確實(shí)能傳到接收方還需要接收方的MAC地址,也就是物理地址壤短。IP地址和MAC地址是一一對(duì)應(yīng)的關(guān)系设拟,一個(gè)網(wǎng)絡(luò)設(shè)備的IP地址可以更換,但是MAC地址一般是固定不變的久脯。**ARP協(xié)議可以將IP地址解析成對(duì)應(yīng)的MAC地址纳胧。**當(dāng)通信的雙方不在同一個(gè)局域網(wǎng)時(shí),需要多次中轉(zhuǎn)才能到達(dá)最終的目標(biāo)帘撰,在中轉(zhuǎn)的過(guò)程中需要通過(guò)下一個(gè)中轉(zhuǎn)站的MAC地址來(lái)搜索下一個(gè)中轉(zhuǎn)目標(biāo)跑慕。
ARP 協(xié)議是網(wǎng)絡(luò)層的協(xié)議,但是它所工作的內(nèi)容是數(shù)據(jù)鏈路層的。
6.服務(wù)器收到處理請(qǐng)求
接收端的服務(wù)器在鏈路層收到數(shù)據(jù)包核行,再層層向上直到應(yīng)用層牢硅,這過(guò)程中包括在運(yùn)輸層通過(guò)TCP協(xié)議講分段的數(shù)據(jù)包重新組成原來(lái)的HTTP請(qǐng)求報(bào)文。服務(wù)接收到客戶端發(fā)送的HTTP請(qǐng)求后芝雪,查找客戶端請(qǐng)求的資源减余,并返回響應(yīng)報(bào)文,響應(yīng)報(bào)文中包括一個(gè)重要的信息——狀態(tài)碼惩系。狀態(tài)碼由三位數(shù)字組成位岔,其中比較常見(jiàn)的是200 OK表示請(qǐng)求成功。301表示永久重定向堡牡,即請(qǐng)求的資源已經(jīng)永久轉(zhuǎn)移到新的位置赃承。在返回301狀態(tài)碼的同時(shí),響應(yīng)報(bào)文也會(huì)附帶重定向的url悴侵,客戶端接收到后將http請(qǐng)求的url做相應(yīng)的改變?cè)僦匦掳l(fā)送瞧剖。404 not found 表示客戶端請(qǐng)求的資源找不到
7.服務(wù)器發(fā)送HTTP響應(yīng)報(bào)文,瀏覽器收到服務(wù)器響應(yīng)可免,得到html代碼
服務(wù)接收到客戶端發(fā)送的HTTP請(qǐng)求后抓于,查找客戶端請(qǐng)求的資源,并返回響應(yīng)報(bào)文浇借,響應(yīng)報(bào)文中包括一個(gè)重要的信息——狀態(tài)碼捉撮。狀態(tài)碼由三位數(shù)字組成,其中比較常見(jiàn)的是200 OK表示請(qǐng)求成功妇垢。301表示永久重定向巾遭,即請(qǐng)求的資源已經(jīng)永久轉(zhuǎn)移到新的位置。在返回301狀態(tài)碼的同時(shí)闯估,響應(yīng)報(bào)文也會(huì)附帶重定向的url灼舍,客戶端接收到后將http請(qǐng)求的url做相應(yīng)的改變?cè)僦匦掳l(fā)送。404 not found 表示客戶端請(qǐng)求的資源找不到涨薪。
接下來(lái)瀏覽器就要進(jìn)行頁(yè)面渲染了~~~
頁(yè)面渲染
頁(yè)面渲染基本流程:解析html以構(gòu)建dom樹(shù)->構(gòu)建render樹(shù)->布局render樹(shù)-> 繪制render樹(shù)
先上圖:
由圖可知骑素,瀏覽器會(huì)解析三種東西:
(1)HTML/SVG/XHTML,解析這三種文件會(huì)產(chǎn)生一個(gè)DOM Tree刚夺。DOM Tree的構(gòu)建是一個(gè)深度遍歷的過(guò)程:當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)都構(gòu)建好之后才會(huì)構(gòu)建當(dāng)前節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn)献丑。
(2)CSS,解析CSS會(huì)產(chǎn)生CSS樹(shù)侠姑。
(3)JavaScript腳本创橄,主要是通過(guò)DOM API 和 CSSOM API來(lái)操作 DOM Tree和 CSS Rule Tree。根據(jù)DOM樹(shù)和CSSOM來(lái)構(gòu)造Rendering Tree莽红。注意啦~~Rendering Tree渲染樹(shù)并不等同于DOM樹(shù)妥畏,因?yàn)橐恍┫馠eader或者display:none的東西就沒(méi)有必要放在渲染樹(shù)中了。
有了Render Tree,瀏覽器已經(jīng)知道網(wǎng)頁(yè)中有哪些節(jié)點(diǎn)咖熟,各個(gè)節(jié)點(diǎn)的CSS定義以及他們的從屬關(guān)系圃酵,下一步操作稱之為L(zhǎng)ayout,即計(jì)算尺每個(gè)節(jié)點(diǎn)在屏幕中的位置馍管。最后一步就是繪制了
重點(diǎn)來(lái)啦:
《how browsers work》里面講過(guò)一句話:上述這個(gè)過(guò)程是逐步完成的郭赐,為了更好的用戶體驗(yàn),渲染引擎將會(huì)盡可能早的將內(nèi)容呈現(xiàn)到屏幕上确沸,并不會(huì)等到所有的html都解析完成之后再去構(gòu)建和布局render樹(shù)捌锭。它是解析完一部分內(nèi)容就顯示一部分內(nèi)容,同時(shí)罗捎,可能還在通過(guò)網(wǎng)絡(luò)下載其余內(nèi)容观谦。
在網(wǎng)上找到下面這篇關(guān)于HTML頁(yè)面加載和解析流程的文章,感覺(jué)不錯(cuò)桨菜,自帶小板凳豁状,過(guò)來(lái)吃瓜~~~
科普一下兩個(gè)概念:
(1)Reflow(回流):瀏覽器要花時(shí)間去渲染,當(dāng)它發(fā)現(xiàn)了某個(gè)部分發(fā)生了變化影響了布局倒得,那就需要倒回去重新渲染泻红。 比如:頁(yè)面初次渲染;瀏覽器窗口大小改變霞掺,DOM結(jié)構(gòu)變化谊路; render樹(shù)變化,某些元素的尺寸菩彬,未知缠劝,內(nèi)容變了;元素字體大小改變骗灶,激活CSS偽類(如:hover)惨恭。
(2)Repaint(重繪):如果只是改變了某個(gè)元素的背景顏色,文字顏色等矿卑,不影響元素周圍或內(nèi)部布局的屬性喉恋,將只會(huì)引起瀏覽器的repaint,重畫某一部分母廷。
回流一定伴隨著重繪,而重繪卻可以單獨(dú)出現(xiàn)糊肤,Reflow要比Repaint更花費(fèi)時(shí)間琴昆,也就更影響性能。所以在寫代碼的時(shí)候馆揉,要盡量避免過(guò)多的Reflow业舍。
我們需要努力減少reflow/repaint,可通過(guò)下面的方式:
(1)不要一條一條地修改 DOM 的樣式。與其這樣舷暮,還不如預(yù)先定義好 css 的 class态罪,然后修改 DOM 的 className。?
(2)不要把 DOM 結(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量下面。?
(3)為動(dòng)畫的 HTML 元件使用 fixed 或 absoult 的 position复颈,那么修改他們的 CSS 是不會(huì) reflow 的。?
(4)千萬(wàn)不要使用 table 布局沥割。因?yàn)榭赡芎苄〉囊粋€(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局耗啦。
編寫css時(shí)應(yīng)當(dāng)注意:
CSS選擇符是從右到左進(jìn)行匹配的。從右到左机杜!所以帜讲,#nav li 我們以為這是一條很簡(jiǎn)單的規(guī)則,秒秒鐘就能匹配到想要的元素椒拗,但是似将,但是,但是蚀苛,是從右往左匹配啊在验,所以,會(huì)去找所有的li枉阵,然后再去確定它的父元素是不是#nav译红。因此,寫css的時(shí)候需要注意:
(1)dom深度盡量淺兴溜。
(2)減少inline javascript侦厚、css的數(shù)量。
(3)使用現(xiàn)代合法的css屬性拙徽。
(4)不要為id選擇器指定類名或是標(biāo)簽刨沦,因?yàn)閕d可以唯一確定一個(gè)元素。
(5)避免后代選擇符膘怕,盡量使用子選擇符想诅。原因:子元素匹配符的概率要大于后代元素匹配符。后代選擇符;#tp p{} 子選擇符:#tp>p{}
(6)避免使用通配符岛心,舉一個(gè)例子来破,.mod .hd *{font-size:14px;} 根據(jù)匹配順序,將首先匹配通配符,也就是說(shuō)先匹配出通配符,然后匹配.hd(就是要對(duì)dom樹(shù)上的所有節(jié)點(diǎn)進(jìn)行遍歷他的父級(jí)元素),然后匹配.mod,這樣的性能耗費(fèi)可想而知.
關(guān)于Script標(biāo)簽:
我們大多會(huì)將script標(biāo)簽放在body結(jié)束標(biāo)簽之前,為啥呀忘古?徘禁?
(1)js代碼在加載完成后,是立即執(zhí)行的
(2)js在執(zhí)行時(shí)會(huì)阻塞頁(yè)面后續(xù)內(nèi)容(包括頁(yè)面的渲染髓堪、其它資源的下載)即會(huì)阻塞Dom樹(shù)的構(gòu)建送朱,原因:因?yàn)闉g覽器需要一個(gè)穩(wěn)定的DOM樹(shù)結(jié)構(gòu)娘荡,而JS中很有可能有代碼直接改變了DOM樹(shù)結(jié)構(gòu),比如使用 document.write 或 appendChild,甚至是直接使用的location.href進(jìn)行跳轉(zhuǎn)驶沼,瀏覽器為了防止出現(xiàn)JS修 改DOM樹(shù)炮沐,需要重新構(gòu)建DOM樹(shù)的情況,所以 就會(huì)阻塞其他的下載和呈現(xiàn)回怜。
減少JavaScript對(duì)性能的影響的方法:
1. 將所有的script標(biāo)簽放到頁(yè)面底部大年,也就是body閉合標(biāo)簽之前,這能確保在腳本執(zhí)行前頁(yè)面已經(jīng)完成了DOM樹(shù)渲染鹉戚。
2. 盡可能地合并腳本鲜戒。頁(yè)面中的script標(biāo)簽越少,加載也就越快抹凳,響應(yīng)也越迅速遏餐。無(wú)論是外鏈腳本還是內(nèi)嵌腳本都是如此。
3. 采用無(wú)阻塞下載 JavaScript 腳本的方法:?
(1)使用script標(biāo)簽的 defer 屬性(僅適用于 IE 和 Firefox 3.5 以上版本)赢底; script標(biāo)簽的defer屬性規(guī)定了是否對(duì)腳本進(jìn)行延遲失都,直到頁(yè)面加載為止。
(2)使用動(dòng)態(tài)創(chuàng)建的script元素來(lái)下載并執(zhí)行代碼幸冻;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?~~~~~聽(tīng)大神說(shuō)寫博客可以成為小仙女~~~~~