瀏覽器如何工作

前言

最近在做web performance的improvement,瀏覽器性能的提升無非就是實(shí)現(xiàn)以下兩個(gè)目標(biāo):

  • 減少頁面load的時(shí)間
  • 提升頁面交互的流暢性

但是以上兩個(gè)目標(biāo)又會經(jīng)常被兩個(gè)issue影響:

  • Network Latency:Load Time會被Latency影響
    Network Latency是指瀏覽器將第一個(gè)請求發(fā)出(HTML 請求)到取回response所花時(shí)間。因此只要這個(gè)Network Latency越短,那么Load time就會越短

  • Browser Single Thread:交互的流暢性會因?yàn)闉g覽器是單線程

瀏覽器是單線程的,主線程要負(fù)責(zé)很多的工作,為了使交互更加流程,就必須減少主線程的responsibility。

但是主線程需要負(fù)責(zé)頁面render以及用戶交互這兩個(gè)很重要的task,一旦主線程陷入了rerender的工作,那么就沒有capacity去處理交互,導(dǎo)致頁面交互不流暢。

瀏覽器render流程

當(dāng)你在瀏覽器input一個(gè)URL,此時(shí)瀏覽器會經(jīng)歷三個(gè)階段:

  • Connection Establish: 和server建立安全連接
  • Parse:解析獲取的HTML 文件
  • Render: 將所有的node 繪制在頁面上

Connection Establish

First Step: DNS Lookup

第一步找到URL所對應(yīng)的IP地址(也就是HTML資源所在的server)。

如果輸入的URL是https://example.com(域名), 并且你從來沒有訪問過這個(gè)域名,那么DNS Lookup就會開始,找到這個(gè)域名對應(yīng)的IP地址。

你的瀏覽器會請求一個(gè)DNS Look服務(wù),也就是發(fā)請求給Name server, 這個(gè)server返回的response中包含IP地址。在第一個(gè)請求之后,這個(gè)IP地址會被瀏覽器cache一段時(shí)間,那么接下來發(fā)送給這個(gè)域名的請求可以直接沖cache中拿,而不需要給Name server發(fā)請求了。

我們需要為不同的域名進(jìn)行DNS Look服務(wù),這一部分可能會對web performance產(chǎn)生影響,尤其是對mobile如果你的頁面上有很多來自不同域名的assets,當(dāng)你使用mobile請求頁面,那么DNS Look的請求需要先到cell tower,然后到phone company, 最后到達(dá)Name server,這會造成Network Latency。

image.png

Second Step: TCP Handshake

一旦瀏覽器獲取到了IP地址,那么瀏覽器就會嘗試和Server建立一個(gè)TCP Connection通過TCP三次握手協(xié)議。

Third Step: TLS Negotiation

通過HTTPS建立的安全連接,需要另一種handshake,我們把它叫做TLS negotiation。 這個(gè)階段是在開始實(shí)際數(shù)據(jù)傳輸之前,決定將使用哪種密碼來加密通信,驗(yàn)證服務(wù)器。目的是為了建立一個(gè)安全的通信環(huán)境。

雖然這個(gè)過程會導(dǎo)致page load的時(shí)間增長,但是一個(gè)secure connection更重要。

Fourth Step: Send Request

上面幾個(gè)步驟做完之后,現(xiàn)在瀏覽器和server之間已經(jīng)建立一個(gè)安全的連接,這時(shí)候?yàn)g覽器就可以和Server進(jìn)行通信了。

這時(shí)候?yàn)g覽器會代表User向server發(fā)請求,請求HTML文件render頁面。

Note,這里有一個(gè)14kb rule的概念,如果想了解可以看Doc

Parsing

一旦HTML請求發(fā)出,瀏覽器接受到the first chunk of data(第一個(gè)TCP Packets), 就會立刻開始解析data, 然后嘗試?yán)L制頁面。

Parse這個(gè)步驟就是將Response解析成DOM和CSSOM,然后用來繪制頁面。

即使請求頁面的HTML大于初始的14KB data packets,瀏覽器也會開始解析并嘗試根據(jù)其數(shù)據(jù)來繪制頁面,因此瀏覽器可能花費(fèi)200ms獲取到了第一個(gè)data packets,然后開始解析繪制頁面,直到first packets處理完畢,之后瀏覽器才會擴(kuò)大其congestion window 擁塞窗口并繼續(xù)傳遞更多數(shù)據(jù)。

因此提升web performance,減少load time的一個(gè)重要方式就是:

在first 14kb的data packets中包含load當(dāng)前頁面所需要的所有data。假設(shè)你的load首屏需要15kb,那么就需要等待200ms先拿到first 14kb,解析然后render,但是由于數(shù)據(jù)不夠,所以頁面上仍然無法呈現(xiàn)內(nèi)容,還需要再次等待200ms獲取下一個(gè)1kb,然后再解析render,才能出現(xiàn)首屏的內(nèi)容。

First Step: Building Dom Tree

將剛剛拿到的first packets of HTML file進(jìn)行處理解析,然后構(gòu)建DOM Tree。這個(gè)步驟是critical rendering path中的第一個(gè)步驟

Block Resource

瀏覽器parser會逐行解析HTML,根據(jù)遇到的HTML tag創(chuàng)建對應(yīng)的DOM Node。但是一旦Parser遇到一些特殊類型的Tag,會做出不同的行為。

瀏覽器的資源主要分成兩類:

  • non-blocking resource

不會阻塞HTML parse和render的resource。 比如圖片、字體。 瀏覽器一旦parse到類似這樣的資源,會發(fā)一個(gè)request去請求這些資源,但是會繼續(xù)進(jìn)行當(dāng)前的parse工作,而不會受到這些資源的block。

  • blocking resource
    blocking resource分為兩種:

    • parse block: 會阻塞HTML的parse工作。比如script(without defer/async/pre scan)。 瀏覽器在parse HTML的過程中,如果遇到script標(biāo)簽,并且標(biāo)簽上并沒有async/defer的標(biāo)簽, 那么就會立刻終止parse工作,發(fā)請求獲取JS文件,并且會一直等待response回來,直接parse JS, 直到JS文件parse完畢,才會重新開始執(zhí)行HTML的parse工作。

    • render block: 會阻塞HTML的render工作。比如css(without pre-load)。css文件不會阻塞parser,如果瀏覽器在parse的過程中遇到link 標(biāo)簽,也只是fire一個(gè)request,而不會停止parse工作。但是在瀏覽器開始render之前,必須load css文件并且執(zhí)行完畢生成CSSOM Tree。

這個(gè)步驟的最終產(chǎn)物就是DOM Tree, 但是由于script標(biāo)簽會block DOM Tree的構(gòu)建,因此這個(gè)步驟的性能瓶頸通常都是script標(biāo)簽。因此我們可以采?。?/p>

  • 給script標(biāo)簽加上defer/async,使得script不會再阻塞parse,并且會等到DOM生成之后再執(zhí)行

  • 使用pre-load,減少主線程等待JS response回來的時(shí)間。<link rel="preload" href="main.js" as="script">

At the meantime: Preload scanner

瀏覽器本身具有同時(shí)發(fā)出多個(gè)請求的能力,因此出現(xiàn)了pre scanner

當(dāng)主線程在忙著構(gòu)建DOM Tree的時(shí)候,Preload scanner(不會占據(jù)主線程)會parse當(dāng)前HTML文件,遇到高優(yōu)先級的資源(如:css、js、font),會提前發(fā)請求獲取資源,這些資源的請求可以同時(shí)發(fā)出,并且不會影響主線程的工作。

多虧Preload scanner,我們不需要等到主線程parse到某一個(gè)JS文件時(shí)才發(fā)出請求獲取JS。

Preload scanner會在后臺執(zhí)行發(fā)請求的操作,因此當(dāng)主線程parse到某一個(gè)資源的時(shí)候,很有可能這個(gè)資源已經(jīng)download完畢,或者正在download。

Second Step: Building CSSOM Tree

處理css文件,構(gòu)建CSSOM Tree。這個(gè)步驟是非常非??斓模虼颂嵘齪erformance通常不會考慮這一步,因?yàn)檫@個(gè)步驟甚至都沒有DNS Lookup費(fèi)時(shí)。這個(gè)步驟是critical rendering path中的第二個(gè)步驟

Render

render step包含style, layout, paint, compositing步驟,也就是將上一個(gè)步驟產(chǎn)生的CSSOM以及DOM render在頁面上。

First step: Building Render Tree

這個(gè)步驟就是將DOM TreeCSSOM Tree進(jìn)行combine,最終生成Render Tree。這步驟是critical rendering path中的第三步。

瀏覽器的主線程會遍歷DOM Tree中的所有可視Node,最終生成Render Tree。對于那些不會展示在頁面上的Node都會不會出現(xiàn)在最終的render Tree中,比如<head> 或者{display: none},請注意{visibility: hidden}這種node會出現(xiàn)在render tree中,因?yàn)檫@種node雖然不顯示但是會占據(jù)空間。

Second Step: Layout

這步驟就是根據(jù)上一步驟產(chǎn)生的Render Tree, 計(jì)算render tree上每一個(gè)node的寬度高度以及在頁面上的位置。這步驟是critical rendering path中的第四步。

Reflow 也是計(jì)算頁面的layout,也就是第一次Layout之后的任何重新Layout都被叫做reflow。一個(gè)節(jié)點(diǎn)的reflow會重新計(jì)算這個(gè)node的寬高和position,也會觸發(fā)他的子孫節(jié)點(diǎn)以及兄弟節(jié)點(diǎn)的reflow。 reflow之后一定會發(fā)生repaint。 reflow是非常昂貴的操作,很費(fèi)時(shí)間,但是又很容易被觸發(fā),因此提升performance的一個(gè)方式就是盡量減少reflow。下面有一些方式可以提升:

  • 在mobile browser中使用viewport meta tag

提前給mobile的browser設(shè)置好viewport, 這樣頁面第一次在mobile browser做layout的時(shí)候,就可以直接使用你設(shè)置好的viewport布局而不需要再次reflow

  • 給Images預(yù)設(shè)高寬

頁面在第一次做layout的時(shí)候,你的image可能還沒有拿回來,本身應(yīng)該放置image的地方?jīng)]有任何寬高,但是一旦image回來,就需要做reflow,因?yàn)閕mage肯定有寬高,會導(dǎo)致其他所有元素的布局都需要重新計(jì)算,這種reflow是非常影響瀏覽器性能的。

我們可以給img標(biāo)簽設(shè)置寬高,這樣image回來之后,不會導(dǎo)致image周圍的其他的元素reflow。

Third Step: Paint

這個(gè)步驟就是將render tree上的所有node繪制到瀏覽器上。 第一次的paint被叫做the first meaningful paint。這步驟是critical rendering path中的第五步。

為了確保scroll或者animation的流暢,因此所有reflow和repain的工作必須在小于16ms完成。

在看一個(gè)動(dòng)畫的時(shí)候,只有每16ms變化一次畫面, 對于人眼而言,才不會感覺有卡頓

Interactivity

當(dāng)頁面繪制完畢,onLoad event被觸發(fā)之后,看似主線程就完全空閑下來了。但事實(shí)上不是這樣的,一些被延遲執(zhí)行的JS文件,可能會在時(shí)候被執(zhí)行,也就意味著主線程會忙著執(zhí)行JS文件。因此根本沒有閑工夫處理scroll touch 等交互操作,在用戶角度就好像頁面卡了。

Time to Interactive(TTI)指代的是頁面上的第一個(gè)請求發(fā)出(HTML請求)到頁面可以和用戶交互所用的時(shí)間。是一個(gè)重要的web performance的measurement metric, 這個(gè)值越小,也就意味著對用戶而言,頁面就是越早的可用。這個(gè)值最好小于50ms。

如果主線程一直忙于解析,編譯和執(zhí)行JavaScript,則該主線程將不可用,因此無法及時(shí)(少于50ms)響應(yīng)用戶交互。

這一部分就是盡量減少主線程的使用。

Doc

https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work

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

友情鏈接更多精彩內(nèi)容