文章內(nèi)容較多,某些未涉及內(nèi)容,可結(jié)合我的另一篇文章(http://www.reibang.com/p/f8ae03a295a2)一起看,有意外收獲.
一豹爹、進(jìn)程和線程
進(jìn)程是操作系統(tǒng)資源分配的最小單位,進(jìn)程包含線程
線程是受進(jìn)程管理的,瀏覽器采用的是多進(jìn)程模式
日常中我們使用瀏覽器是基于一個(gè)一個(gè)tab頁(yè)來進(jìn)行訪問網(wǎng)站,如果說某一個(gè)tab頁(yè)掛掉了,對(duì)于其他tab頁(yè)是沒有任何影響的,說明每一個(gè)tab頁(yè)都是一個(gè)單獨(dú)的進(jìn)程硬纤,他們之間相互獨(dú)立,互不影響.
瀏覽器中的進(jìn)程:
瀏覽器中的進(jìn)程分為5個(gè):
1.瀏覽器進(jìn)程:你可以理解瀏覽器進(jìn)程是一個(gè)統(tǒng)一的‘調(diào)度大師‘译红,去調(diào)度其他進(jìn)程,比如我們?cè)诘刂窓谥休斎雞rl時(shí),瀏覽器進(jìn)程首先會(huì)調(diào)度網(wǎng)絡(luò)進(jìn)程,他可以做一些子進(jìn)程管理以及一些存儲(chǔ)的處理.
2.渲染進(jìn)程:這個(gè)進(jìn)程對(duì)于我們來說是最重要的一個(gè)進(jìn)程,每一個(gè)tab頁(yè)都有一個(gè)獨(dú)立的渲染進(jìn)程,他的主要作用是渲染頁(yè)面.
3.網(wǎng)絡(luò)進(jìn)程:這個(gè)進(jìn)程是控制對(duì)于一些靜態(tài)資源的請(qǐng)求,它將資源請(qǐng)求完成之后交給渲染進(jìn)程進(jìn)行渲染.
4.GPU進(jìn)程:這個(gè)進(jìn)程可以調(diào)用硬件進(jìn)行渲染,從而實(shí)現(xiàn)渲染加速.比如translate3d等css3屬性會(huì)騙取調(diào)用GPU進(jìn)程從而開啟硬件加速.
5.插件進(jìn)程:chrome中的插件也是一個(gè)獨(dú)立的進(jìn)程
各個(gè)進(jìn)程之間是相互獨(dú)立,互不影響的.
從輸入url到頁(yè)面顯示之間究竟發(fā)生了什么?
?http://www.reibang.com/p/f8ae03a295a2
二、網(wǎng)絡(luò)資源層面
首先我們先拋開瀏覽器對(duì)于資源的處理過程,先來看看一次正常的url輸入在資源加載方面經(jīng)歷的生命周期.
當(dāng)我們?cè)诘刂窓谥休斎胍粋€(gè)url時(shí),瀏覽器進(jìn)程會(huì)監(jiān)聽到這次交互.緊接著它會(huì)分配一個(gè)渲染進(jìn)程準(zhǔn)備渲染頁(yè)面,同時(shí)瀏覽器進(jìn)程會(huì)調(diào)用網(wǎng)絡(luò)進(jìn)程加載資源.
等網(wǎng)絡(luò)進(jìn)程加載完資源后會(huì)將資源交給渲染進(jìn)程進(jìn)行頁(yè)面渲染.從進(jìn)程的角度來說整體的加載流程就是這樣.
大的方面來說就是瀏覽器進(jìn)程進(jìn)行調(diào)度,網(wǎng)絡(luò)進(jìn)程加載完資源后交給渲染進(jìn)程進(jìn)行渲染加載的資源.
接下來我們?cè)敿?xì)看看輸入url之后的請(qǐng)求過程中究竟發(fā)生了哪些事情.
網(wǎng)絡(luò)七層協(xié)議
我們可以將這七層歸為下列四層:
應(yīng)用層:通常我們會(huì)將應(yīng)用層奴迅、表示層材义、會(huì)話層統(tǒng)稱為應(yīng)用層,應(yīng)用層的主要協(xié)議是http協(xié)議.
傳輸層:傳輸層中我們?yōu)g覽器中http協(xié)議是基于tcp去進(jìn)行網(wǎng)絡(luò)傳輸.(常見傳輸協(xié)議的有tcp還有udp)
網(wǎng)絡(luò)層:網(wǎng)絡(luò)層中一般都是ip協(xié)議.
物理層:當(dāng)然在數(shù)據(jù)鏈路層和物理層都被稱為物理層.
我們先從7層協(xié)議來分析一下瀏覽器對(duì)于url加載的過程
首先當(dāng)我們輸入url輸入一個(gè)域名,瀏覽器會(huì)在磁盤/內(nèi)存緩存中去查找請(qǐng)求的文件,查找是否命中緩存.如果命中緩存則直接會(huì)從緩存中拿到對(duì)應(yīng)的ip地址.
如果命中緩存則會(huì)直接返回對(duì)應(yīng)資源不會(huì)進(jìn)入下面的步驟
瀏覽器緩存:http://www.reibang.com/p/ccb9c60354a8
這里我們先忽略緩存帶來的影響,這里涉及一個(gè)協(xié)商緩存和強(qiáng)制緩存的知識(shí)點(diǎn)在下面的知識(shí)點(diǎn)中進(jìn)行講解.
假設(shè)我們首次訪問這個(gè)頁(yè)面,此時(shí)并沒有任何緩存:
如果我們?cè)L問的這個(gè)域名沒有被解析過,那么我們需要解析地址欄中輸入的域名.解析域名主要依賴的是DNS協(xié)議,將域名解析為ip地址. ip地址才能找到域名對(duì)應(yīng)的ip.
dns你可以理解它為一個(gè)映射表,將域名和ip地址進(jìn)行映射,其實(shí)就是一個(gè)分布式的數(shù)據(jù)庫(kù),通過域名查找到對(duì)應(yīng)的ip地址.
需要注意的是dns解析是基于udp協(xié)議而非tcp協(xié)議.
這里有個(gè)小問題需要提一下,為什么dns解析時(shí)基于udp而非tcp協(xié)議?
我們的dns解析過程是一個(gè)服務(wù)器的查找過程.因?yàn)橛蛎譃橐患?jí)/二級(jí)...域名,所以每一級(jí)域名都會(huì)迭代去查詢,如果它采用tcp協(xié)議的話,每經(jīng)過一次域名查詢,域名服務(wù)器都會(huì)經(jīng)過三次握手.但是udp就不會(huì),他會(huì)直接發(fā)包然后確認(rèn)
相較于udp,tcp是更加安全,可靠的(因?yàn)槿挝帐忠约八拇螕]手)但是這也造成了它相較于udp消耗更多時(shí)間.
udp常用的場(chǎng)景是視頻或者直播中,對(duì)于我們來說dns解析中使用的udp更多的原因是因?yàn)閡dp的速度,當(dāng)然即使丟包了,我們重新發(fā)送就可以了.
tcp傳輸?shù)倪^程稱為分段傳輸,也就是會(huì)拆分為多個(gè)包,一個(gè)包一個(gè)包的進(jìn)行發(fā)送得到響應(yīng)之后就會(huì)發(fā)送下一個(gè)包.這樣的方式無(wú)疑更加可靠和安全,但是在實(shí)效上并不如udp協(xié)議的實(shí)時(shí)(直接通信無(wú)需建立連接)
此時(shí)會(huì)根據(jù)dns解析通過域名+端口號(hào)解析出對(duì)應(yīng)的ip地址.
我們擁有了ip地址之后,接下來我們就需要將利用ip進(jìn)行尋找網(wǎng)頁(yè)地址
此時(shí)如果我們的請(qǐng)求地址時(shí)候https,在通過ip尋址之后會(huì)額外增加一步ssl協(xié)商保證數(shù)據(jù)的安全性
當(dāng)IP地址尋址成功后,瀏覽器知道了服務(wù)器的地址,此時(shí)并不會(huì)立即將數(shù)據(jù)發(fā)送過去,而是會(huì)進(jìn)入一個(gè)排隊(duì)的等待過程,比如一個(gè)域名下有多個(gè)請(qǐng)求,同一個(gè)域名在http1.1下最多只建立6個(gè)tcp鏈接,也就說同一個(gè)時(shí)間最多發(fā)送6個(gè)請(qǐng)求,他們首先會(huì)進(jìn)入一個(gè)排隊(duì)的等待時(shí)間.
排隊(duì)結(jié)束后開始發(fā)送請(qǐng)求.此時(shí)就需要通過tcp先進(jìn)性創(chuàng)建鏈接通過三次握手,建立完成鏈接后傳輸數(shù)據(jù).
上邊我們說過tcp是基于分段傳輸?shù)?基于內(nèi)容特別大的傳輸內(nèi)容tcp會(huì)將數(shù)據(jù)包進(jìn)行拆分成為多個(gè)數(shù)據(jù)包進(jìn)行有序傳輸.
在tcp的傳輸過程中如果傳輸中出現(xiàn)了丟包,那么tcp會(huì)進(jìn)行重傳.
服務(wù)器接收到之后會(huì)按照順序進(jìn)行接收.
tcp建立完鏈接之后,瀏覽器會(huì)通過http請(qǐng)求發(fā)送請(qǐng)求數(shù)據(jù)
一次http請(qǐng)求包含
請(qǐng)求行
請(qǐng)求頭
請(qǐng)求體
在http1.1中默認(rèn)開啟了connection:keep-alive,它的作用是在下次發(fā)送請(qǐng)求時(shí)在一定時(shí)間內(nèi)可以復(fù)用上一次的tcp鏈接而不需要重新建立鏈接.(也就是在一定時(shí)間內(nèi)保持相同域名tcp鏈接不斷開).
此時(shí)服務(wù)器接收到請(qǐng)求發(fā)送的數(shù)據(jù),根據(jù)請(qǐng)求行均抽、請(qǐng)求頭、請(qǐng)求體進(jìn)行解析,解析完成后返回相應(yīng)行其掂、響應(yīng)頭油挥、響應(yīng)體.
注意:這里服務(wù)器返回狀態(tài)碼中有一些特殊的狀態(tài)碼
301/302這兩個(gè)狀態(tài)碼都是表示重定向,如果返回這兩個(gè)任意一個(gè),就會(huì)根據(jù)響應(yīng)頭中的location返回的域名重新進(jìn)行之前操作(https://blog.csdn.net/qq_43968080/article/details/107355758)
304狀態(tài)碼表示瀏覽器本次資源走緩存而不會(huì)重新請(qǐng)求下載資源.
這個(gè)過程便是一個(gè)最基礎(chǔ)的瀏覽器針對(duì)一個(gè)url訪問網(wǎng)絡(luò)請(qǐng)求的過程。
以taobao.com為例讓我們一探究竟
上邊說了那么多枯燥的理論款熬,接下來讓我們?cè)趯?shí)際中去體會(huì)一下深寥。
首先我們打開一個(gè)全新的瀏覽器tab頁(yè)在地址欄輸入taobao.com
因?yàn)槲沂鞘状芜M(jìn)入這個(gè)頁(yè)面,所以并沒有任何緩存贤牛。前邊說到過瀏覽器進(jìn)程首先會(huì)開啟一個(gè)頁(yè)面渲染進(jìn)程惋鹅,同時(shí)開啟網(wǎng)絡(luò)進(jìn)程去請(qǐng)求。
首先讓我們打開chrome開發(fā)者工具:
建議大家在新的無(wú)痕瀏覽頁(yè)中去進(jìn)行這些操作殉簸,我們排除掉DNS緩存以及任何瀏覽器緩存的干擾機(jī)制去看結(jié)果會(huì)更加純粹闰集。
每一次重定向都會(huì)重新進(jìn)行DNS解析以及TCP連接的建立是非常耗時(shí)的。所以在我們的真實(shí)項(xiàng)目中要盡量的避免進(jìn)行資源重定向般卑,如果有存在重定向的資源盡量還是將它直接替換成新的地址連接武鲁。
接下來我們以第三次https://www.taobao.com/這次請(qǐng)求為例來分析一下一次請(qǐng)求(無(wú)任何緩存)的各個(gè)階段:
分析一次請(qǐng)求完整的瀑布圖所代表的含義
我們先來看看對(duì)應(yīng)chrome中的瀑布圖:
Queueing?這個(gè)階段表示排隊(duì)階段,瀏覽器在以下情況下對(duì)請(qǐng)求進(jìn)行排隊(duì):
有更高優(yōu)先級(jí)的請(qǐng)求椭微。
已經(jīng)為此源打開了六個(gè) TCP 連接洞坑,這是限制。僅適用于 HTTP/1.0 和 HTTP/1.1蝇率。
瀏覽器在磁盤緩存中短暫分配空間
Stalled?表示停滯不前迟杂,請(qǐng)求可能因上述排隊(duì)中描述的任何原因而停止。(比如說鏈接開始后本慕,會(huì)進(jìn)行一些tcp連接的復(fù)用處理一些代理相關(guān)的邏輯)
DNS Lookup?這一步就表示開始進(jìn)行DNS解析排拷,將我們的請(qǐng)求域解析為ip地址。
Initial connection?這階段表示我們進(jìn)行tcp鏈接/重試和ssl協(xié)商共同耗費(fèi)的時(shí)間锅尘。
SSL?這一步就是當(dāng)我們請(qǐng)求https域名時(shí)會(huì)進(jìn)行ssl協(xié)商的耗時(shí)监氢。
request sent?表示請(qǐng)求開始發(fā)送
watting for server代表?Time To First Byte布蔗。此時(shí)間包括 1 次往返延遲和服務(wù)器準(zhǔn)備響應(yīng)所用的時(shí)間。通俗來說就是當(dāng)我們請(qǐng)求發(fā)送到接受到響應(yīng)的第一個(gè)字節(jié)的時(shí)間浪腐。
waitting for server?這一步通匙葑幔可以粗略表示本次請(qǐng)求服務(wù)器(后臺(tái))從接受到請(qǐng)求然后返回響應(yīng)結(jié)果處理的耗時(shí)。
Content Download?就不必多說了议街,是我們下載本地響應(yīng)的時(shí)間泽谨。
同時(shí)對(duì)于chrome而言在http1.1下同一個(gè)域的最多支持并發(fā)6個(gè)TCP鏈接,注意這里是TCP鏈接而不是HTTP請(qǐng)求特漩。
我們用一個(gè)小例子來說明下吧雹,在同一個(gè)TCP連接里面,先發(fā)送A請(qǐng)求涂身,然后等待服務(wù)器做出回應(yīng)雄卷,收到后再發(fā)出B請(qǐng)求。管道機(jī)制則是允許瀏覽器同時(shí)發(fā)出A請(qǐng)求和B請(qǐng)求蛤售,但是服務(wù)器還是按照順序丁鹉,先回應(yīng)A請(qǐng)求,完成后再回應(yīng)B請(qǐng)求(https://www.zhihu.com/pin/1681622411215228928?utm_id=0)悍抑。
http發(fā)展的各個(gè)階段
http 0.9
? ?最早時(shí)候只支持傳輸html鳄炉,請(qǐng)求中沒有任何請(qǐng)求頭。
http 1.0?
? ? 引入了請(qǐng)求頭和響應(yīng)頭搜骡,這樣的話就可以根據(jù)請(qǐng)求頭區(qū)分傳輸?shù)膬?nèi)容是圖片還是html又或是js。
http 1.1?
? ? ?針對(duì)http1.0每一次請(qǐng)求都會(huì)發(fā)送請(qǐng)求建立tcp鏈接佑女,請(qǐng)求結(jié)束后斷開tcp鏈接记靡。這無(wú)疑是非常耗時(shí)的。所以在http 1.1中默認(rèn)開啟了一個(gè)請(qǐng)求頭connect:keep-alive進(jìn)行在一個(gè)tcp鏈接的復(fù)用团驱。當(dāng)然即使引入了長(zhǎng)鏈接keep-alive摸吠,還存在一個(gè)問題就是基于http 1.0中是一個(gè)請(qǐng)求發(fā)送得到響應(yīng)后才開始發(fā)送下一個(gè)請(qǐng)求,針對(duì)這個(gè)機(jī)制1.1提出了管線化pipelining機(jī)制嚎花,但是需要注意的是服務(wù)器對(duì)應(yīng)同一tcp鏈接上的請(qǐng)求是一個(gè)一個(gè)去處理的寸痢,所以這就會(huì)導(dǎo)致一個(gè)比較嚴(yán)重的問題隊(duì)頭阻塞。
如果說第一個(gè)發(fā)送的請(qǐng)求丟包了紊选,那么服務(wù)器會(huì)等待這個(gè)請(qǐng)求重新發(fā)送過來在進(jìn)行返回處理啼止。之后才會(huì)處理下一個(gè)請(qǐng)求。即使瀏覽器是基于pipelining去多個(gè)請(qǐng)求同時(shí)發(fā)送的兵罢。
http 2.0
? ? ?提出了很多個(gè)優(yōu)化點(diǎn)献烦,其中最著名的就是解決了http1.1中的隊(duì)頭阻塞問題。
? ? ? ?HTTP/2復(fù)用TCP連接則不同卖词,雖然依然遵循請(qǐng)求-響應(yīng)模式巩那,但客戶端發(fā)送多個(gè)請(qǐng)求和服務(wù)端給出多個(gè)響應(yīng)的順序不受限制,這樣既避免了"隊(duì)頭堵塞",又能更快獲取響應(yīng)即横。在復(fù)用同一個(gè)TCP連接時(shí)噪生,服務(wù)器同時(shí)(或先后)收到了A、B兩個(gè)請(qǐng)求东囚,先回應(yīng)A請(qǐng)求跺嗽,但由于處理過程非常耗時(shí),于是就發(fā)送A請(qǐng)求已經(jīng)處理好的部分舔庶, 接著回應(yīng)B請(qǐng)求抛蚁,完成后,再發(fā)送A請(qǐng)求剩下的部分惕橙。HTTP/2長(zhǎng)連接可以理解成全雙工的協(xié)議
http 2.0 的特點(diǎn):
多路復(fù)用: 支持使用同一個(gè)tcp鏈接瞧甩,基于二進(jìn)制分幀層進(jìn)行發(fā)送多個(gè)請(qǐng)求,支持同時(shí)發(fā)送多個(gè)請(qǐng)求,同時(shí)服務(wù)器也可以處理不同順序的請(qǐng)求而不必按照請(qǐng)每個(gè)請(qǐng)求的順序進(jìn)行處理返回弥鹦。這就解決了http 1.1中的隊(duì)頭阻塞問題肚逸。
頭部壓縮: 在http2協(xié)議中對(duì)于請(qǐng)求頭進(jìn)行了壓縮達(dá)到提交傳輸性能。
Server push:?http2中支持通過服務(wù)端主動(dòng)推送給客戶端對(duì)應(yīng)的資源從而讓瀏覽器提前下載緩存對(duì)應(yīng)資源彬坏。
http3.0:
? ? ?基于tcp下就難免存在阻塞問題朦促,如果發(fā)生丟包就需要等待上一個(gè)包。在http3徹底解決了tcp的隊(duì)頭阻塞問題栓始,它是基于udp協(xié)議并且在上層增加了一層QUIC協(xié)議务冕。
關(guān)于http 3.0和2.0這部分我研究的不是很多,所以就不做詳細(xì)的對(duì)比了幻赚。大家如果有更詳細(xì)的建議可以在評(píng)論區(qū)留言禀忆。后續(xù)如果有必要我會(huì)補(bǔ)充這部分內(nèi)容。
關(guān)于http 1.1的pipelining機(jī)制和http 2.0的多路復(fù)用落恼,他們究竟存在什么區(qū)別箩退?
HTTP/1.0 without pipelining: 必須響應(yīng) TCP 連接上的每個(gè) HTTP 請(qǐng)求,然后才能發(fā)出下一個(gè)請(qǐng)求佳谦。HTTP/1.1 with pipelining: 可以立即發(fā)出 TCP 連接上的每個(gè) HTTP 請(qǐng)求戴涝,而無(wú)需等待前一個(gè)請(qǐng)求的響應(yīng)返回。響應(yīng)將以相同的順序返回钻蔑。
HTTP/2 multiplexing:?TCP 連接上的每個(gè) HTTP 請(qǐng)求都可以立即發(fā)出啥刻,而無(wú)需等待先前的響應(yīng)返回∈概铮可分幀先按照順序返回處理好的部分內(nèi)容郑什,接著返回下一個(gè)請(qǐng)求等等,在返回之前處理好沒返回的內(nèi)容
Summary:HTTP/1.1 最大的變化就是引入了持久連接(persistent connection)蒲肋,在HTTP/1.1中默認(rèn)開啟 Connection: keep-alive蘑拯,即TCP連接默認(rèn)不關(guān)閉钝满,可以被多個(gè)請(qǐng)求復(fù)用。
? ? ? ? ? 那么管道機(jī)制就是在同一個(gè)TCP連接中可以同時(shí)發(fā)送多個(gè)HTTP請(qǐng)求而不用等待上一個(gè)請(qǐng)求返回?cái)?shù)據(jù)后申窘,再發(fā)送下一個(gè)請(qǐng)求弯蚜。雖然可以同時(shí)發(fā)送多個(gè)HTTP請(qǐng)求,但是服務(wù)器響應(yīng)是按照請(qǐng)求的順序進(jìn)行響應(yīng)的剃法。
? ? ? ? ? HTTP 2.0的多路復(fù)用是在同一個(gè)TCP連接中碎捺,可以發(fā)送多個(gè)HTTP請(qǐng)求,而且請(qǐng)求的響應(yīng)不依賴于前一個(gè)請(qǐng)求贷洲。每個(gè)請(qǐng)求單獨(dú)處理收厨,不會(huì)出現(xiàn)HTTP1.1中上一個(gè)請(qǐng)求沒有回應(yīng)便一直等待的情況。
三优构、瀏覽器渲染
首先我們先來看一看關(guān)于瀏覽器加載資源的粗略圖
參考?https://zhuanlan.zhihu.com/p/575565899
1诵叁、渲染進(jìn)程
? ? ? 渲染進(jìn)程有GUI線程、js引擎線程钦椭、定時(shí)器觸發(fā)線程拧额、異步HTTP請(qǐng)求線程、事件觸發(fā)線程彪腔,參考下圖了解每個(gè)線程的作用侥锦,以及各個(gè)線程相互協(xié)作完成渲染的過程。
2德挣、GUI線程的工作
渲染過程描述
?解析html以構(gòu)建DOM樹 -> 構(gòu)建render樹 ->布局render樹 -> 繪制render樹
? 首先恭垦,當(dāng)瀏覽器拿到html文檔時(shí)首先會(huì)進(jìn)行HTML文檔解析,構(gòu)建DOM樹
? 其次格嗅,遇到css樣式如link標(biāo)簽或者style標(biāo)簽時(shí)會(huì)解析css署照,構(gòu)建樣式樹
HTML解析構(gòu)建和css的解析構(gòu)建是相互獨(dú)立的并不會(huì)造成沖突,因此我們通常將css樣式放在head中吗浩,讓瀏覽器盡早解析css
再次,當(dāng)html的解析遇到scrip標(biāo)簽没隘,就會(huì)停止DOM樹的解析懂扼,開始下載js
因?yàn)閖s是會(huì)堵塞html解析的,是堵塞資源右蒲,其原因在于js可能會(huì)改變html現(xiàn)有結(jié)構(gòu)阀湿,將控制權(quán)移交給javascript引擎;等javascript引擎運(yùn)行完畢瑰妄,瀏覽器會(huì)從中斷的地方恢復(fù)DOM構(gòu)建陷嘴。而因此就會(huì)推遲頁(yè)面首繪的時(shí)間〖渥可以在首繪不需要js的情況下用async和defer實(shí)現(xiàn)異步加載灾挨,這樣js就不會(huì)阻塞html的解析了邑退。
注意:異步執(zhí)行是指下載。執(zhí)行js時(shí)仍然會(huì)堵塞劳澄。當(dāng)html解析完成之后地技,瀏覽器會(huì)將文檔標(biāo)注為交互狀態(tài),并開始解析那些處于defferred模式的腳本秒拔,也就是那些應(yīng)該在文檔解析完后才執(zhí)行的腳本莫矗。然后,文檔狀態(tài)將設(shè)置為完成砂缩,一個(gè)加載事件將隨之觸發(fā)作谚。
再一次,在得到DOM樹和樣式樹后就可以進(jìn)行渲染樹的構(gòu)建了
應(yīng)該注意的是渲染樹和DOM元素相對(duì)應(yīng)的庵芭,但并非一一對(duì)應(yīng)妹懒,比如非可視化的DOM元素不會(huì)插入呈現(xiàn)樹中,例如‘head’元素喳挑,如果元素的display屬性為‘none’彬伦,那么也不會(huì)顯示在呈現(xiàn)樹中(但是visibility屬性值為hidden的元素仍會(huì)顯示)
再一次,渲染樹構(gòu)建完畢后將會(huì)進(jìn)行布局
布局使用流模型的layout算法伊诵。所謂流模型即是指Layout的過程只需要進(jìn)行一遍即可单绑。但實(shí)際實(shí)現(xiàn)中,流模型會(huì)有例外曹宴,Layout是一個(gè)遞歸的過程吁讨,每個(gè)節(jié)點(diǎn)都負(fù)責(zé)自己及其子節(jié)點(diǎn)的Layout。Layout結(jié)果是相對(duì)父節(jié)點(diǎn)的坐標(biāo)和尺寸页衙。其過程可以簡(jiǎn)述為:
(1)父節(jié)點(diǎn)確定自己的寬度
(2)父節(jié)點(diǎn)完成子節(jié)點(diǎn)放置疟位,確定其相對(duì)坐標(biāo)?
?(3)節(jié)點(diǎn)確定自己的寬度和高度
(4)父節(jié)點(diǎn)根據(jù)所有的子結(jié)點(diǎn)高度
最后,render Tree已經(jīng)構(gòu)建完成
瀏覽器渲染樹引擎并不直接使用渲染樹進(jìn)行繪制版扩,為了方便處理定位(裁剪)废离,溢出滾動(dòng)(頁(yè)內(nèi)滾動(dòng)),css轉(zhuǎn)換/不透明/濾鏡礁芦,蒙版或反射蜻韭,Z(Z排序)等,瀏覽器需要生成另外一棵樹 - 層樹柿扣。
因此繪制過程如下:
? ? ?獲取DOM并將其分割為多個(gè)層(RenderLayer)將每個(gè)層?xùn)鸥窕し剑ⅹ?dú)立的繪制進(jìn)位圖中將這些位圖作為文理上傳至GPU復(fù)合多個(gè)層來生成最終的屏幕圖像(終極layer)
3、問題知識(shí)點(diǎn)匯總未状?
(1)關(guān)于瀏覽器渲染的容易誤解點(diǎn)總結(jié)
1俯画、DOM樹的構(gòu)建是邊加載邊解析的.
? ? ? DOM樹的構(gòu)建是從接收到文檔開始的,一邊會(huì)將字節(jié)轉(zhuǎn)化為字符司草,字符轉(zhuǎn)化為標(biāo)記艰垂,標(biāo)記構(gòu)建dom樹
? ? ? 這個(gè)構(gòu)成被分為標(biāo)記化和構(gòu)建化
? ? ? 這是個(gè)漸進(jìn)的過程泡仗,為達(dá)到更好的用戶體驗(yàn),呈現(xiàn)引擎會(huì)力求盡快將內(nèi)容顯示在屏幕上材泄。它不必等到整個(gè)HTML文檔解析完畢之后沮焕,就會(huì)開始構(gòu)建呈現(xiàn)樹和設(shè)置布局。在不斷接收和處理來自網(wǎng)絡(luò)的其余內(nèi)容的同時(shí)拉宗,呈現(xiàn)引擎會(huì)將部分內(nèi)容解析并顯示出來峦树。
參考文檔:http://taligarsiel.com/Projects/howbrowserswork1.htm2、渲染樹的構(gòu)建是一邊加載旦事,一邊解析魁巩,一邊渲染?
? ? ? 這三個(gè)過程在實(shí)際進(jìn)行的時(shí)候又不是完全獨(dú)立姐浮,而是會(huì)有交叉谷遂。會(huì)造成一邊加載,一邊解析卖鲤,一邊渲染的工作現(xiàn)象肾扰。參考文檔:http://www.reibang.com/p/2d522fc2a8f83、css的標(biāo)簽嵌套越多蛋逾,越不容易定位到元素
? ? ? css的解析是自右至左逆向解析的集晚,嵌套越多越增加瀏覽器的工作量,而不會(huì)越快区匣。
? ? ?因?yàn)槿绻蚪馕鐾蛋危纭竏iv div p em」,我們首先就要檢查當(dāng)前元素到 html 的整條路徑亏钩,找到最上層的 div莲绰,再往下找,如果遇到不匹配就必須回到最上層那個(gè) div姑丑,往下再去匹配選擇器中的第一個(gè) div蛤签,回溯若干次才能確定匹配與否,效率很低栅哀。
? ? ? 逆向匹配則不同顷啼,如果當(dāng)前的 DOM 元素是 div,而不是 selector 最后的 em昌屉,那只要一步就能排除。只有在匹配時(shí)茵瀑,才會(huì)不斷向上找父節(jié)點(diǎn)進(jìn)行驗(yàn)證间驮。打個(gè)比如 p span.showing
? ? ? ?你認(rèn)為從一個(gè)p元素下面找到所有的span元素并判斷是否有class showing快,還是找到所有的span元素判斷是否有class showing并且包括一個(gè)p父元素快
(2) css與js對(duì)于dom的影響
? ? ? ?js加載和解析是會(huì)阻塞后續(xù)dom的解析马昨。
? ? ? ?css加載會(huì)阻塞頁(yè)面的渲染竞帽。
? ? ? ?css加載不會(huì)阻塞后續(xù)dom解析(https://blog.csdn.net/qq_41462647/article/details/130161709)
? ? ? ?css加載會(huì)阻塞后續(xù)js的執(zhí)行
css是否會(huì)阻塞Dom扛施?
1.對(duì)于css的加載是不阻塞dom的構(gòu)建的。
2.對(duì)于css的加載時(shí)會(huì)阻塞之后的dom節(jié)點(diǎn)的渲染的(需要把樣式加載完之后再渲染)屹篓。js是否會(huì)阻塞Dom疙渣?
其實(shí)毋庸置疑,js的執(zhí)行過程一定是會(huì)阻塞Dom Tree和Css OM的堆巧。
其實(shí)這里大家只要把握一個(gè)原則妄荔,在渲染進(jìn)程中JS線程和渲染線程是互斥的關(guān)系。
為什么css放上邊而js放在下面谍肤?
上邊我們講到了css的加載和解析并不會(huì)阻塞Dom的構(gòu)建啦租,但是會(huì)阻塞頁(yè)面上之后元素的渲染。這也就造成了如果css放在頂部的話荒揣,后續(xù)Dom元素的渲染需要依賴本次css代碼執(zhí)行解析完成之后才會(huì)渲染篷角。
將css放在底部的話頁(yè)面的確是會(huì)產(chǎn)生兩次渲染的。但是第一次沒有任何樣式的渲染其實(shí)是一次“無(wú)效渲染”系任。
我們利用chrome瀏覽器performance去分析將css放在底部的代碼中發(fā)現(xiàn)實(shí)際上瀏覽器進(jìn)行了兩次元素的繪制恳蹲,也就是說如果將css代碼放在底部是會(huì)發(fā)生重繪(以及可能會(huì)引發(fā)回流),這個(gè)操作是非常耗時(shí)的一個(gè)過程俩滥。
所以將css放在頂部的話:頁(yè)面首次渲染瀏覽器僅僅會(huì)進(jìn)行一次渲染嘉蕾,而不會(huì)造成多余的重繪和回流步驟。
為什么js需要放在底部举农?
上邊我們說到了關(guān)于js實(shí)際上是會(huì)阻塞Dom Tree的構(gòu)建和渲染的荆针。同時(shí)js依賴于前邊的css文件加載完成后才會(huì)進(jìn)行執(zhí)行。保證js可以操作樣式的颁糟。所以css之后如果存在js那么css的加載過程也是可以間接性的阻塞DCL事件的航背。
將js放在了元素之前,首先在js執(zhí)行完成之前是不會(huì)進(jìn)行后續(xù)元素的構(gòu)建和渲染的棱貌。只有等待js加載并且解析完成之后渲染線程才會(huì)繼續(xù)之后的Dom Tree的構(gòu)建以及頁(yè)面的渲染
這里額外有一點(diǎn):在頁(yè)面解析Html之前瀏覽器會(huì)額外掃描外部鏈接玖媚,將外部鏈接交給網(wǎng)絡(luò)進(jìn)程進(jìn)行下載。所以css和js的下載可以是并行的婚脱。所以今魔,我們之所以將js放在底部。是因?yàn)閖s放在底部是會(huì)等待頁(yè)面渲染完畢后再去執(zhí)行后續(xù)js障贸。
defer和async這兩個(gè)屬性的不同
參考:https://juejin.cn/post/6894629999215640583
? ? ? ? ??https://zhuanlan.zhihu.com/p/622763093
參考:https://zhuanlan.zhihu.com/p/458664335