在學習前端的過程當中,你們都會對瀏覽器這個神祕的盒子感到好奇
從輸入一串url
到頁面解析渲染完成,瀏覽器都幹了些啥?css
爲了更好的理解這個過程,咱們使用一個工具來幫助咱們Chrome
自帶的開發工具中的performance
(老版本和其餘瀏覽器爲timeline
)html
點擊錄製,而後在地址欄輸入咱們的url
這裏以百度爲例,輸入www.baidu.com
,回車
而後點擊錄製結束,就能看到這個東西前端
咱們最早看到的是這個git
字面意思就能夠理解,瀏覽器在向服務器發送請求,並接收響應頭和響應體
不過實際上在這以前,瀏覽器還作了一些事
得先知道朝哪一個服務器發送請求吧github
url
,先去本地DNS
緩存列表裏尋找對應的服務器的ip地址和端口號
,若沒有找到,繼續尋找系統緩存和路由緩存,若找到則跳轉第三步DNS
服務器,沒有就將域名發送給其餘服務器,遞歸尋找,從根域名服務器開始不斷向下遞歸,直到返回對應的IP地址和端口號
,並將其緩存ip地址和端口號
,與目標服務器創建TCP
鏈接(三次握手)這三步並無被咱們看見,而後接下來的事就被咱們觀察到了
瀏覽器向服務器發送http
請求,並接收返回的響應頭和響應體segmentfault
繼續往下看數組
黃色部分都是瀏覽器的一些默認行爲,其中包括隱藏原本的標籤頁內容等等
以及下面的Recalculate Style
(從新計算樣式),Layout
(重排)是爲清空原本的頁面,爲新頁面作準備
這些不用管它,不過在這中間咱們又一次看到了Receive Data
那是由於服務器在發送數據的時候,可能會進行拆包,分幾回發送瀏覽器
瀏覽器比較勤快,它並不會等html
徹底接收完纔開始解析,而是接受一部分就開始解析一部分緩存
HTML Parser
的任務是將HTML
標記,解析成DOM Tree
,這是一個深度遍歷的過程,只有Dom
下的子節點都被遍歷完成,才遍歷下一個同級Dom
節點性能優化
同時,在解析的過程當中,若是遇到了圖片,link
標籤,script
標籤,都會向服務器發送請求
例如咱們上圖,遇到了百度的logo
圖片,就請求下載
遇到js
就當即解析下載執行執行
不論是內聯的仍是外部的,都會阻塞後續dom
的解析和渲染
因此通常將<script>
標籤放在<body>
後面
若是是外部的,還能夠在<script>
標籤上加上defer
或async
屬性defer
可讓js的下載不影響html的後續解析,且在html解析完了再執行js文件,且按照原來的下載順序async
也是讓js的下載不影響html的後續解析,但一旦下載完了就當即執行,所以也沒法保證按照下載順序執行
遇到內聯的css
樣式就開始解析
外部的css
文件,則在接收到了以後進行解析
二者都會不會阻塞Dom
的解析,但會阻塞Dom
的渲染
這也是爲何要把css
的<link>
標籤放進<head>
中
當css文件放在<head>中時,雖然css解析也會阻塞後續dom的渲染,可是在解析css的同時也在解析dom,因此等到css解析完畢就會逐步的渲染頁面了
把css
語句解析成爲CSSOM
在有了Dom Tree
和CSSOM
以後
瀏覽器就會構建Render Tree
(渲染樹)
其實把DOM Tree和CSSOM
進行附加
因此Render Tree
實際上就是一個計算好了樣式,同時不包含display:none
之類,不佔據空間的元素的的渲染樹。
而後瀏覽器就根據這個Render Tree
進行第一次的佈局(Layout
)以及繪製(Paint
)
完成了第一次繪製以後,瀏覽器會繼續收到服務器發來的數據
圖片,css
文件,js
文件
其中可能會有致使頁面佈局更改或樣式更改的內容,都會添加到咱們的Render Tree上去
引發重排(Layout
)或重繪(Paint
)
其中重排必定會連帶着引發重繪,反之則否則
引發重排的操做
頁面首次渲染
瀏覽器窗口大小發生改變
元素尺寸或位置發生改變
元素內容變化(文字數量或圖片大小等等)
元素字體大小變化
添加或者刪除可見的DOM元素
激活CSS僞類(例如::hover)
設置style屬性
那是遇到一次就重排一次嗎?也不是
瀏覽器會有一個渲染隊列來進行優化
積累了必定量或必定時間的更改內容,纔會進行重排
不過當訪問了特定屬性的時候
好比js
文件裏遇到一句console.log(body.clientWidth)
會強制刷新渲染隊列
如下屬性的訪問會當即刷新渲染隊列
widthheight | margin | padding | display | border |
position | overflow | clientWidth | clientHeight | clientTop |
clientLeft | offsetWidth | offsetHeight | offsetTop | offsetLeft |
scrollWidth | scrollHeight | scrollTop | scrollLeft | scrollIntoView() |
scrollTo() | getComputedStyle() | getBoundingClientRect() | scrollIntoViewIfNeeded() |
因此訪問這些屬性要謹慎,最好分離讀寫,以避免過多的重排影響性能
引發重繪的通常都是修改元素的屬性
color | border-style | visibility | background |
text-decoration | background-image | background-position | background-repeat |
outline-color | outline | outline-style | border-radius |
outline-width | box-shadow | background-size |
從性能優化的角度,咱們要儘可能減小瀏覽器的重排和重繪,尤爲是重排
DOM
操做,同一個DOM
的多個屬性改變能夠寫在一塊兒,在一個局部方法中須要屢次訪問同一個Dom
,則先暫存它的引用。批量添加DOM
,能夠先讓元素脫離文檔流,操做完後再帶入文檔流,這樣只會觸發一次重排(應用fragment
元素)position
屬性設爲absolute
或fixed
,例若有動畫效果的元素API
替代消費高的API
,轉換優化消費高的集合,如用querySelectorAll()
替代getElementByXX()
。GPU
加速HTML
集合(類數組)來遍歷,由於集合遍歷比真數組遍歷耗費更高。style
屬性,由於經過每一次設置都會觸發一次reflow
,因此最好是使用class
屬性reflow
過於頻繁table
佈局,由於table
中某個元素旦觸發了reflow
,那麼整個表格都會reflow
。能夠設置table-layout:auto
;或者是table-layout:fixed
這樣能夠只reflow
一行,JavaScript
代碼修改元素樣式,儘可能使用修改class
名方式操做樣式或動畫;你們能夠本身去嘗試一下performance
的使用,能夠更直觀的理解瀏覽器的工做流程
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...
https://sylvanassun.github.io...
https://www.imooc.com/article...
https://segmentfault.com/a/11...