瀏覽器輸入url后的流程

綜述

??以Chrome瀏覽器為例,Chromium多進程架構模型在打開瀏覽器的時候,就會啟動瀏覽器進程(用以管理其他進程和維護瀏覽器的狀態(tài)),多個網(wǎng)絡進程,多個渲染進程,GPU進程,插件進程。在一個tab頁,一般來說都有一個渲染進程,但是也可能出現(xiàn)一個tab頁中多個渲染進程和多個tab頁共用一個渲染進程的情況。我們先大致說明瀏覽器從輸入URL之后的過程,隨后再進行每個模塊的進一步討論。

整體流程

??大致簡明流程如下:
????1 交付給網(wǎng)絡進程處理
????2 若是域名,則首先進行DNS解析工作(無處不在的緩存,資源緩存,DNS緩存等<1>);按照Url協(xié)議頭進行HTTP連接建立(HTTP的隊首阻塞<2>,HTTP2的優(yōu)勢等)
????3 獲取到對應數(shù)據(jù),我們以HTML資源為例(Content-type識別資源類型和415的狀態(tài)碼<3>),網(wǎng)絡進程下載文件到瀏覽器內存中,隨后IO進程通知渲染進程進行渲染(進程間通信<4>)
????4 渲染進程接受到HTML,就調用對應加載器進行網(wǎng)絡進程下載下來的資源的加載(load<5>),隨后調用解釋器進行解釋工作(詞法分析)。html從上往下進行解析工作,其中同樣會涉及到JS和CSS的加載和解析(<6>)
????5 head頭部處理完之后,就正式進入DOM樹的解釋創(chuàng)建過程了,這里html解釋器會先進行token詞語分割工作,也就是tokens的生成,之后在tokens的基礎上再計算出來dom的樹形結構數(shù)據(jù),以chrome為例,這里也涉及著兩個算法:符號識別算法(The tokenization algorithm)和 樹的構建算法(Tree construction algorithm)(<7>)。我們知道瀏覽器渲染進程處理的渲染,然而在解析構建cssom和dom的時候確實并行的,這里主要的原因可以看成css是并不會影響dom樹的結構的,但是在進入render tree的合并的時候,cssom又是阻塞的。同樣的css由css加載器加載,css解析器解析,歷經(jīng)轉化->詞法分析tokens->cssom樹形數(shù)據(jù)構造的流程,css我們在書寫的時候就是仿照對象的格式書寫的,所以這里在tokens分割的時候就較簡潔一點了,我們既然說是tree數(shù)據(jù)結構,那么瀏覽器是怎么計算出來css中聲明的各項樣式的父子關系的呢?這里我們可以大膽假設一下,我們知道通常在書寫css樣式的時候,針對css的選擇器一般是id class tag等,這里偽類和偽元素先不說,畢竟其前面書寫的時候緊緊跟著作用的元素的,我們按照上述三種常用的選擇器來進行假設,cssom可以是多個樹嗎?或者是多個數(shù)據(jù)結構表示的嗎?像id聲明的樣式,我們完全可以使用一個Map或者Set的數(shù)據(jù)結構進行構建數(shù)據(jù)的嘛,class為主的樣式在聲明的時候我們如果按照子類選擇器的書寫來看,class的樣式確實可以形成以body為根節(jié)點的樹形結構數(shù)據(jù)的,但是我們如果都書寫成單個class的樣式呢(重復率不高且優(yōu)化css文件大小),這樣理論上來說我們也是只能得到一個高度為2的樹了。再說tag,和class同理,如果我們不使用嵌套的格式和來,那么處理過程和class可能是一致的,這可能也是css提倡按照子類選擇器的格式書寫的原因吧。這樣看來我們所說的css樹完全沒有什么優(yōu)勢了嘛,時間復雜度O(N)的一個查找過程顯然是劣于Map/Set的O(1)的查找的。假設之后我們就需要進一步反問,css構建的數(shù)據(jù)結構真的是樹嗎?這里感謝 https://zhuanlan.zhihu.com/p/25380611 的源碼層面的css介紹,可以看到根據(jù)css文件構造出來的是 id、class、標簽、偽類選擇器 的四個Map,確實在查找多的場景下,適當?shù)臓奚稽c空間能換取時間加速也是極好的,一切為了速度[/笑哭],dom樹的樣式賦值的速度。
????6 終于經(jīng)過dom樹和css樣式Map同時都構建完成之后,就進入了render tree的合并階段了,理論上來說這里就只是一個樹的遍歷和Map的key索引和render tree節(jié)點的創(chuàng)建過程了
????7 render tree完成之后,主線程就進入到了布局計算的階段了(存疑:render tree不能和layer cal同步進行嘛),這個階段的產(chǎn)物就是render layer tree。布局計算顧名思義就是確定每個dom的位置和大小(回流和重繪<8>),此時的依據(jù)就是盒模型,盒模型分為塊級元素,行內元素,替換元素,非替換元素和伸縮盒元素,替換元素指的就是以img video為例的元素,它們的位置定位是可以忽略src的,在資源準備好渲染的時候直接替換元素的盒內容;非替換元素一般就指span這類的文本元素。我們先戰(zhàn)術忽略box-sizing的對標準盒子和怪異盒子的區(qū)別,塊級元素在寬度計算上,默認是充滿父盒子的,同時css顯示聲明的話寬度就按照聲明的來,當然單位一定要是瀏覽器能識別的: px em ex ch等,而塊級元素的高度呢,則是有聲明按照聲明,沒有的話按照盒子內元素的高度疊加計算和最大值取值得到的。行內元素的寬度計算則是依賴于字體的字寬大小和white-space計算出來對應的em,高度則是依賴于字體的fontsize,寬高都是依賴字體(行內元素)。說到行內元素,就不得不提到input,img這種行內元素了,在上面也說到過,其實替換元素,可以設置寬高的,所以在計算寬高的時候也是類似塊級的計算盒子的尺寸的,不過又因為其替換行為,就可能導致圖片移除盒子的現(xiàn)象,不過計算溢出盒子也是不影響后續(xù)布局的,這里側面說明替換元素的盒子計算不是依賴替換之后的資源的尺寸的。寬高的計算可以按照上述的表述,而布局的計算則不得不提到文檔流和文本流的概念了,渲染線程在計算布局的時候,對不加改變聲明的元素是按照文檔流從上往下,文本流從左往右進行布局計算的,如同上述所說的塊級元素獨占一行,多個行內元素在同一行布局,既然說到文檔流,那么就當然有脫離文檔流的方式了,這里我們大致提一下:fixed、absolute,float設置后都可以脫離當前的文檔流,對應的元素在計算的時候就要重新選擇基準點的位置進行布局了。同時在布局計算中,也涉及到上下文的切換和不同的計算模式:BFC(塊級格式化上下文 Block Formatting Context),IFC(行內格式化上下文 Inline Formatting Context),這兩種不同的格式化上下文在計算盒子的大小和邊距上有著對應的規(guī)則,BFC中元素是塊級元素,當然默認是按照文檔流進行豎直的排列,豎直方向上的邊距通過marigin來進行設置,同時存在著邊距重疊的現(xiàn)象;排列的時候第一個塊級元素的marigin的左基線是父級元素的border-left;針對float可以避免其重疊,同時高度計算上也是受內float元素的高度影響的;同時對布局上比較重要的特性:BFC是塊級隔離域,在計算外盒子的大小和定位的時候,內層的排列方式可以不用考慮的。那么怎么設置成BFC呢:float、position不是static和relative的、display的值是inline-block、table-cell、flex、table-caption或者inline-flex、overflow不是visiable的都可以將外層的盒子聲明成一個BFC,看到這些設置,我們也會意識到,浮動元素在丟失高度的時候為外層設置overflow為hidden的時候可以指定出來高度的依據(jù)了。當然IFC對于行內元素的布局計算也是有著重要的作用的,IFC外層同樣是一個塊級區(qū)域,里面的行內元素是按照行one by one的排列的,當出現(xiàn)一行放不下的時候,就拆分成兩個塊級區(qū)域,分為兩行展示,同時兩行之前沒有間隙,總而言之,行內元素顯示的時候默認是按照"抱團取暖"的,只有開發(fā)者可以拆散[/尷尬],值得一提的是,面試的時候可能也會問到,行內元素可以設置margin,padding和border嗎?從上面的BFC和行內元素尺寸計算中,我們可以得出,marigin在左右上是可以設置的,也是生效的,padding同樣在左右上是會生效的,上下方向設置了也是可以看出來效果的,但是并不能影響外層盒子的高度的,border是可以生效的。自此layer樹開始構建了,這里同樣要提到一下層疊上下文的概念,以webkit為例,其在渲染的時候本身就采用的是分層構建渲染最終合并重疊顯示的機制的,布局樹在形成的過程中,主線程也同時創(chuàng)建了一個虛擬的繪圖上下文開始進行層級的繪制工作,這里提一下為什么要分層進行繪制,第一我們在編寫代碼的時候可能用到z-index的css屬性,這時候瀏覽器為了實現(xiàn)層疊的效果,是需要將元素繪制在單獨的一層的,這樣方便后期的層級調整,同樣我們如果設置了translateZ的時候,當前元素可能在Z軸發(fā)生變換,也是需要單獨繪制成一層的,這里就解釋了為什么要用transfom來替換top等的第一個要點了,可以繪制成單獨的一層,那么GPU加速呢?GPU主要是用于多層級的合成圖像的階段。好了。經(jīng)由上述的虛擬的繪圖上下文的努力之后,最后就是生成對應的圖像了。這里的繪圖實現(xiàn)是很復雜的過程,瀏覽器為了更快的能顯示出來圖像也進行了大量的優(yōu)化實踐。ok繪圖渲染這里,我們可以發(fā)現(xiàn),性能優(yōu)化中可視區(qū)的更快渲染是及其重要的,這里提一下css的一個屬性content-visibility,設置后可以讓元素不出現(xiàn)在屏幕上的時候先跳過對元素的計算和渲染,這樣也可以加快渲染的速度。同樣的使用tranform和opecity等都可以將元素提升成單獨一層并且避免回流。虛擬上下文生成圖像之后就開始顯示到瀏覽器了。
??綜上所述,瀏覽器從url到頁面圖像的顯示的大致過程就理清楚了,讓我們簡化一下整個流程:
???? 網(wǎng)絡相關(DNS解析,HTTP應用層鏈接,構造TCP鏈接,接受HTML流) -> HTML解析過程(HTML首先是字符串流,token分割,dom樹創(chuàng)建,遇到內聯(lián)css就加載和解析,遇到不修飾的js就阻塞加載解釋,cssMap構建,render tree合并(樣式解析賦值),布局計算layer tree,繪制圖像,層組合,顯示圖像),這就是瀏覽器第一次進行加載的大致流程了。

標記點知識

<1> 瀏覽器實現(xiàn)的緩存

????1 DNS緩存:需要解析DNS的地方就會有緩存的存在,本地建立DNS緩存可以有效加快網(wǎng)站的連接速度,可以通過chrome://chrome-urls/查看到其本地的cache
????2 瀏覽器資源緩存:強制緩存和協(xié)商緩存,無狀態(tài)的http協(xié)議想要確定溝通緩存,就需要在headers中附加上相關信息,大致相關的字段是:Request中的Cache-Control、If-Modified-Since和Response中的Cache-Control、Expires、Date、Last-Modified、ETag。
????????強制緩存: 強制緩存http1.0使用的是Expires進行控制的,其表示資源到期時間,瀏覽器在識別資源未達到過期時間的時候,就不會發(fā)送請求到服務端;http1.1中使用Cache-Control來進行控制,主要因為Expires對比的時間是本地和服務器的時間,可能存在本地時間被篡改導致緩存不會命中的情況。Cache-Control是多字段組成的,主要是max-age控制過期時間,public/private控制能否被代理緩存,no-cache使用緩存前去校驗,no-store每次都去服務器請求。在使用max-age的時候,瀏覽器在倒計時范圍內也是不進行資源的請求的。強制緩存是有著極好的節(jié)省帶寬和較少請求的特點的,因為在命中后就連請求都不發(fā)出了,只在內存中或者硬盤上讀取數(shù)據(jù),其缺點也很明顯,在服務端資源發(fā)生改變的時候,客戶端未到到期時間是不會主動去拉取最新數(shù)據(jù)的。
????????協(xié)商緩存:每次請求實際都是要到達服務端的,但是服務端會決定是否返回數(shù)據(jù),節(jié)省的是響應數(shù)據(jù)的body體的傳輸帶寬。協(xié)商緩存主要由Last-Modify/If-Modify-Since和ETag/If-None-Match控制。Last-Modify/If-Modify-Since代表的是上次修改時間,服務器響應時會加上文件的最后修改時間Last-Modify,服務器接受響應的時候會獲取If-Modify-Since識別瀏覽器上的資源的修改時間是否發(fā)生改變,改變的話會返回200和最新的Last-Modify,不改變的話就返回304;同理ETag也是類似的原理,Etag主要解決上述修改時間可能修改內容后不發(fā)生改變的問題,其由文件的inode,文件大小,文件上次修改時間進行防碰撞的hash后的值。inode是linux上識別文件的編號,使用vim會生成swp副本在保存后覆蓋同名文件從而改變inode,這里在docker上是有這個問題的,在docker run -v 將linux上的文件掛載到容器中后,在物理機上vim文件容器是響應不到變化的,但是使用echo和sed是可以響應變化的,這里主要就是因為容器掛載進去的其實是根據(jù)inode和響應改變的,vim之后文件的inode就發(fā)生改變了,自然傳遞不了歷史inode的內容變化了,而echo和sed是內容改變,不會進行inode的修改。

<2> 隊首阻塞

????瀏覽器在實現(xiàn)的時候,按照http1.1協(xié)議來看,Crome同一時刻支持的是6個并發(fā)連接,第七個開始就需要阻塞著等著前六個連接的釋放了,故在頁面實現(xiàn)的時候,盡可能的控制資源的網(wǎng)絡請求數(shù)量也是優(yōu)化中的一部分。不過在http2.0實現(xiàn)上,這個問題就已經(jīng)不會再出現(xiàn)了,下面簡單介紹下http2.0的提升
????????1 IO多路復用:http1.1雖然已經(jīng)較http0.9實現(xiàn)了基于keep-alive為主的tcp連接復用,但是在http請求響應模型和串行文本傳輸數(shù)據(jù)下,http報文的傳輸還是一問一答的形式的,后面的請求在共用的tcp連接上也是不會先于前一個完成的,http2.0引入了二進制數(shù)據(jù)幀和流的概念,在tcp上請求是基于流傳輸?shù)?,同時二進制數(shù)據(jù)幀進行了順序標識,使得流數(shù)據(jù)傳輸可以無序傳輸,在另一端會進行順序合成數(shù)據(jù)的,使用現(xiàn)代計算機的處理器能力來降低數(shù)據(jù)傳輸中的等待和耗損。
????????2 首部壓縮:http協(xié)議在傳輸短數(shù)據(jù)的時候,頭部的占用字節(jié)是遠遠大于數(shù)據(jù)的占用的,雖然瀏覽器可能也會實現(xiàn)http傳輸?shù)牡却途酆?,但是在兩端每次請求都要攜帶大量字節(jié)的頭部信息也是有著很大的損失的。http2.0使用HTTP2頭部壓縮算法對頭部信息進行編碼,并在http的客戶端和服務端同時維護headers對應的hash表,這樣在傳輸中就只用傳輸對應hash表中的key值就可以了。經(jīng)典的空間換時間啊。
????????3 服務端推送:http2.0幀傳輸同樣使得一個請求可以有多個響應。利用服務器推送可以對頁面進一步優(yōu)化,在html獲取的時候,服務端提前解析到html中需要加載的其他資源(js css等),主動推送給瀏覽器。

<3> Content-type

這里引用MDN上的解釋


The Content-Type representation header is used to indicate the original media type of the resource (prior to any content encoding applied for sending).

In responses, a Content-Type header tells the client what the content type of the returned content actually is. Browsers will do MIME sniffing in some cases and will not necessarily follow the value of this header; to prevent this behavior, the header X-Content-Type-Options can be set to nosniff.


????這里說明下415的情況,Unsupported Media Type 在python中使用request包發(fā)送請求的時候,如果不手動設置headers中的Content-type的時候,很容易獲取到這個返回結果,其解決方式其實就是在headers中指定Media Type。


下述未完 待補充中


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容