天天看點

爛梗面試題: 從輸入url到頁面加載完成,發生了什麼

目錄

一、總體流程

二、DNS 域名解析

2.1 DNS 域名解析的過程

三、建立TCP連接配接,發送Http請求資源

3.1 建立 TCP 連接配接

3.2 發送 HTTP 請求,并接收伺服器響應結果,關閉 HTTP 連接配接

四、浏覽器解析HTML

4.1 浏覽器解析過程

五、回流(Reflow)和 重繪(Repaint)

5.1 回流

5.2 重繪

5.3 性能影響

5.4 回流優化

5.5 如何避免

一、總體流程

從浏覽器通路url,到頁面别成功渲染,大緻流程如下:

  1. DNS解析
  2. 浏覽器與目标伺服器建立TCP連接配接
  3. 發送HTTP請求
  4. 伺服器處理請求,并傳回響應封包,斷開TCP連接配接
  5. 浏覽器解析和渲染頁面

二、DNS 域名解析

DNS 域名解析其實就是尋找存儲了想要擷取的資源的那一台伺服器 IP 位址的過程。當你在浏覽器中輸入了一個位址,如:

www.baicu.com,這其實并不是真正的伺服器位址,而是通過這個域名向 DNS 伺服器查詢到這個域名真正指向的 IP 位址。

2.1 DNS 域名解析的過程

  1. 用戶端收到輸入的域名後,先在本地 host 中查找域名對應的 IP 關系,若找到了,則直接向對應的 IP 建立連接配接,擷取資源等。 若沒有找到,則向 DNS 伺服器發送這個域名,請求伺服器解析。
  2. 用戶端輸入域名
  3. 在本地 host 中查找域名對應的 IP 關系,若找到了,直接跳過域名解析過程,繼續2.2步驟
  4. 若本地 host 中沒有域名對應關系,則向本地 DNS 伺服器發送一個含有域名 www.cnblogs.com 的 DNS 查詢封包
  5. 本地伺服器發送域名到跟DNS伺服器,根伺服器注意到 com 字尾,向本地 DNS 伺服器傳回 comDNS 伺服器的位址
  6. 本地伺服器再次向 comDNS伺服器發送查詢請求,comDNS 伺服器注意到其 www.cnblogs.com 字尾并傳回負責該域名的權威DNS伺服器的IP位址
  7. 本地伺服器向權威 DNS 伺服器請求資源,并接收響應
爛梗面試題: 從輸入url到頁面加載完成,發生了什麼

三、建立TCP連接配接,發送Http請求資源

3.1 建立 TCP 連接配接

用戶端成功拿到伺服器位址後,與伺服器建立 TCP 連接配接。

爛梗面試題: 從輸入url到頁面加載完成,發生了什麼

用戶端發送一個帶有SYN标志的資料包給服務端,服務端收到後,回傳一個帶有SYN/ACK标志的資料包以示傳達确認資訊,最後用戶端再回傳一個帶ACK标志的資料包,代表握手結束,連接配接成功。

上圖也可以這麼了解:

用戶端:“你好,在家不,有你快遞。”

服務端:“在的,送來就行。”

用戶端:“好嘞。”

3.2 發送 HTTP 請求,并接收伺服器響應結果,關閉 HTTP 連接配接

用戶端向伺服器發送 get 或 post 請求,擷取資源資訊,并接收伺服器傳回的響應封包。每一個 get 或 post 請求都是一個Http連接配接,都要經曆 建立連接配接 => 請求 => 響應 => 斷開連接配接 這四個過程。

四、浏覽器解析HTML

4.1 浏覽器解析過程

浏覽器是一個邊解析邊渲染的過程。過程如下:

  1. 浏覽器解析HTML檔案建構DOM樹,
  2. 解析CSS檔案建構渲染樹
  3. 渲染樹建構完成後,浏覽器開始布局渲染樹并将其繪制到螢幕上
爛梗面試題: 從輸入url到頁面加載完成,發生了什麼

五、回流(Reflow)和 重繪(Repaint)

在讨論回流與重繪之前,我們要知道:

  1. 浏覽器使用流式布局模型 (Flow Based Layout)。
  2. 浏覽器會把

    HTML

    解析成

    DOM

    ,把

    CSS

    解析成

    CSSOM

    DOM

    CSSOM

    合并就産生了

    Render Tree

  3. 有了

    RenderTree

    ,我們就知道了所有節點的樣式,然後計算他們在頁面上的大小和位置,最後把節點繪制到頁面上。
  4. 由于浏覽器使用流式布局,對

    Render Tree

    的計算通常隻需要周遊一次就可以完成,但

    table

    及其内部元素除外,他們可能需要多次計算,通常要花3倍于同等元素的時間,這也是為什麼要避免使用

    table

    布局的原因之一。
  5. 回流必将引起重繪,重繪不一定會引起回流。

5.1 回流

Render Tree

中部分或全部元素的尺寸、結構、或某些屬性發生改變時,浏覽器重新渲染部分或全部文檔的過程稱為回流。

會導緻回流的操作:

  • 頁面首次渲染
  • 浏覽器視窗大小發生改變
  • 元素尺寸或位置發生改變
  • 元素内容變化(文字數量或圖檔大小等等)
  • 元素字型大小變化
  • 添加或者删除可見的

    DOM

    元素
  • 激活

    CSS

    僞類(例如:

    :hover

  • 查詢某些屬性或調用某些方法

一些常用且會導緻回流的屬性和方法:

  • clientWidth

    clientHeight

    clientTop

    clientLeft

  • offsetWidth

    offsetHeight

    offsetTop

    offsetLeft

  • scrollWidth

    scrollHeight

    scrollTop

    scrollLeft

  • scrollIntoView()

    scrollIntoViewIfNeeded()

  • getComputedStyle()

  • getBoundingClientRect()

  • scrollTo()

5.2 重繪

當頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:

color

background-color

visibility

等),浏覽器會将新樣式賦予給元素并重新繪制它,這個過程稱為重繪。

5.3 性能影響

回流代價比重繪代價大。

有時候一個元素回流,會導緻他的父元素和他相關的元素也回流。

5.4 回流優化

現代浏覽器會對頻繁的回流或重繪進行優化

即浏覽器會維護一個隊列,将所有要回流和重繪的操作放入隊列中,當隊列滿了或是達到時間門檻值後會清理一次隊列,進行一次批處理,這樣可以把多次回流和重繪變成一次。

當你通路以下屬性或方法時,浏覽器會立刻清空隊列:

  • clientWidth

    clientHeight

    clientTop

    clientLeft

  • offsetWidth

    offsetHeight

    offsetTop

    offsetLeft

  • scrollWidth

    scrollHeight

    scrollTop

    scrollLeft

  • width

    height

  • getComputedStyle()

  • getBoundingClientRect()

因為隊列中可能存在影響了這些屬性和方法傳回值的操作,浏覽器為了確定你拿到的資料是最準确的,會先強制清理隊列,進行一次批處理。

5.5 如何避免

5.5.1 CSS

  • 避免使用 table
  • 盡可能在dom樹的末端修改class
  • 避免設定多層内聯樣式
  • 避免使用CSS表達式(如calc)
  • 盡量将動畫效果應用到 position 為 absolute 和 fix 的節點上

5.5.2 JavaScript

  • 避免頻繁操作樣式,最好一次性重寫 style 屬性,或将樣式清單定義在一個class裡,并一次性更改
  • 避免頻繁操作dom,可以建立一個documentFragment,在它上面應用所有dom操作,最後将它添加到文檔中
  • 先為元素設定 display: none,操作結束後再将他顯示出來。因為在

    display

    屬性為

    none

    的元素上進行的

    DOM

    操作不會引發回流和重繪。
  • 避免頻繁讀取會引發回流/重繪的屬性,如果确實需要多次使用,就用一個變量緩存起來。
  • 對具有複雜動畫的元素使用絕對定位,使它脫離文檔流,否則會引起父元素及後續元素頻繁回流。

參考連結:

浏覽器的回流與重繪 (Reflow & Repaint)