瀏覽器是咱們平常開發的重要的工具,那麼你瞭解瀏覽器嗎?即便在前端面試中,咱們也常常會遇到:在瀏覽器地址中從輸入url地址到出現頁面,這個過程發生了什麼?介紹一下重繪和迴流?這一類關於瀏覽器的問題。咱們可能會知道大概的輪廓但對於具體的細節倒是不那麼清楚,那麼今天咱們就從瀏覽器組成開始來了解一下瀏覽器的渲染機制javascript
瀏覽器主要由7個部分組成:css
瀏覽器內核分爲兩部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎html
常見的瀏覽器內核:Trident(IE)、Gecko(火狐)、Blink(Chrome、Opera)、Webkit(Safari)前端
在瞭解瀏覽器渲染過程以前,先來了解一下頁面的加載流程。有助於更好理解後續渲染過程。從瀏覽器地址中從輸入url地址到渲染出一個頁面,會通過如下過程。 1.瀏覽器輸入的url地址通過DNS解析得到對應的IP 2.向服務器發起TCP的3次握手 3.創建連接後,瀏覽器向該IP地址發送http請求 4.服務器接收到請求,返回一堆 HMTL 格式的字符串代碼 5.瀏覽器得到html代碼,解析成DOM樹 6.獲取CSS並構建CSSOM 7.將DOM與CSSOM結合,建立渲染樹 8.找到全部內容都處於網頁的哪一個位置,佈局渲染樹 9.最終繪製出頁面java
咱們將要介紹的瀏覽器渲染過程主要步驟是5-9步,能夠用下面的圖來形象的展現 面試
這個解析過程大概能夠分爲幾個步驟: segmentfault
注意:帶有結束標籤標識的Token不會建立節點對象 第四步:經過「開始標籤」與「結束標籤」來識別並關聯節點之間的關係。當全部Token都生成並消耗完畢後,咱們就獲得了一顆完整的DOM樹。後端
可是如今有一個疑問,節點之間的關聯關係是如何維護的呢? 上面咱們提到Token會標識是「開始標籤」仍是「結束標籤」,如下圖爲例:「Hello」Token位於「title」開始標籤與「title」結束標籤之間,代表「Hello」Token是「title」Token的子節點。同理「title」Token是「head」Token的子節點。 瀏覽器
既然有了html解析,那css解析也是必不可少的,解析css構建CSSOM 的過程和構建DOM的過程很是的類似。當瀏覽器接收到一段CSS,瀏覽器首先要作的是識別出Token,而後構建節點並生成CSSOM 緩存
CSS匹配HTML元素是一個至關複雜和有性能問題的事情。因此,DOM樹要小,CSS儘可能用id和class,千萬不要過渡層疊下去 因此,CSS的加載速度與構建CSSOM的速度將直接影響首屏渲染速度,所以在默認狀況下CSS被視爲阻塞渲染的資源
當咱們生成DOM樹和CSSOM樹後,咱們須要將這兩顆樹合併成渲染樹,在構建渲染樹的過程當中瀏覽器須要作以下工做:
在渲染的過程當中,遇到一個script標記時,就會中止渲染,去請求腳本文件並執行腳本文件,由於瀏覽器渲染和 JS 執行共用一個線程,並且這裏必須是單線程操做,多線程會產生渲染 DOM 衝突。JavaScript的加載、解析與執行會嚴重阻塞DOM的構建。只有等到腳本文件執行完畢,纔會去繼續構建DOM。
js不單會阻塞DOM構建,還會致使CSSOM也阻塞DOM的構建,若是JavaScript腳本還操做了CSSOM,而正好這個CSSOM尚未下載和構建,瀏覽器甚至會延遲腳本執行和構建DOM,直至完成其CSSOM的下載和構建,而後再執行JavaScript,最後在繼續構建DOM
所以script的位置很重要,在實際使用過程當中遵循如下兩個原則:
瀏覽器拿到渲染樹後,就會從渲染樹的根節點開始遍歷,而後肯定每一個節點對象在頁面上的確切大小與位置,一般這一行爲也被稱爲「自動重排」。佈局階段的輸出是一個盒子模型,它會精確地捕獲每一個元素在屏幕內的確切位置與大小,全部相對測量值都將轉換爲屏幕上的絕對像素。這一過程也可稱爲迴流
佈局完成後,瀏覽器會當即發出「Paint Setup」和「Paint」事件,將渲染樹轉換成屏幕上的像素。
在咱們瞭解瀏覽器的渲染機制後,DOM 和 CSSOM 結構構建順序,咱們能夠針對性能優化問題給出一些方案,提高頁面性能。
當元素的樣式發生變化時,瀏覽器須要觸發更新,從新繪製元素。這個過程當中,有兩種類型的操做,即重繪與迴流。
注意:迴流必定會觸發重繪,而重繪不必定會迴流,重繪的開銷較小,迴流的代價較高
所以爲了減小性能優化,咱們能夠儘可能避免迴流或者重繪操做 css
javascript
defer 和 async 屬性的區別:
2)狀況2 (異步下載) async 屬性表示異步執行引入的 JavaScript,與 defer 的區別在於,若是已經加載好,就會開始執行——不管此刻是 HTML 解析階段仍是 DOMContentLoaded 觸發以後。須要注意的是,這種方式加載的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發以前或以後執行,但必定在 load 觸發以前執行。
3)狀況3 <scriptdefersrc="script.js">(延遲執行) defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 加載時 HTML 並未中止解析,這兩個過程是並行的。整個 document 解析完畢且 defer-script 也加載完成以後(這兩件事情的順序無關),會執行全部由 defer-script 加載的 JavaScript 代碼,而後觸發 DOMContentLoaded 事件。
defer 與相比普通 script,有兩點區別:
js優化能夠在script標籤加上 defer屬性 和 async屬性用於在不阻塞頁面文檔解析的前提下,控制腳本的下載和執行
其餘: CSS 標籤的 rel屬性 中的屬性值設置爲 preload 可以讓你在你的HTML頁面中能夠指明哪些資源是在頁面加載完成後即刻須要的,最優的配置加載順序,提升渲染性能
咱們已經將瀏覽器的渲染機制瞭解了一遍,不只瞭解到一些性能優化方案,也能夠得出結論: 瀏覽器渲染的關鍵路徑共分五個步驟:
構建DOM -> 構建CSSOM -> 構建渲染樹 -> 佈局 -> 繪製
參考連接