作者:小土豆
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公眾號:不知名寶藏程序媛(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)
碼字不易,點贊鼓勵喲~
一.前言
我們熟知的瀏覽器的主要功能就是向服務器發送請求,然后顯示服務器返回的資源。
而這里的資源一般指的就是HTML文檔。
(當然資源還包括xml、圖片)
那瀏覽器是如何將一個HTML文檔解析為我們眼睛看到的各種圖形和顏色呢。
這便是我們要討論的瀏覽器的渲染機制了。
關於瀏覽器的渲染機制,是面試會被問到的高頻考點。
所以話不多說,一起探究一下這個問題。
備注:瀏覽器的渲染機制是由瀏覽器的渲染引擎決定的。
瀏覽器的渲染引擎也就是我們常說的瀏覽器的內核。
比如常見的IE的內核-Trident,Firefox的內核-Gecko,chrome的內核-Webkit等。
由於不同瀏覽器的內核實現存在差異,因此同一份HTML文檔在不同瀏覽器上的解析結果也會有細微的差距。
本篇文章不會細致到不同內核的差異。
二.瀏覽器的渲染線程
我們都知道一份HTML文檔中一般會包含HTML標簽、CSS樣式、JavaScript腳本這些基礎的內容。
所以瀏覽器為了處理HTML文檔,設計了一系列的線程,分別為:
GUI渲染線程
JS引擎線程
定時觸發器線程
事件處理線程
異步http請求線程
下面簡單的了解一下這些線程在解析HTML文檔中都分別負責那些任務。
GUI渲染線程
GUI-圖形用戶界面,那GUI渲染線程也就是負責渲染瀏覽器界面上顯示的內容。
即負責解析HTML標簽和CSS樣式。
在瀏覽器的五個渲染流程中,GUI渲染線程均參與其中。
JS引擎線程
JS引擎線程顧名思義就是負責處理JavaScript腳本的線程。
我們都知道JS存在同步任務和異步任務。
而JS引擎線程是一個單線程,所以JS引擎在執行JavaScript代碼的時候主線程會維護一個執行棧,除了執行棧之外還會維護一個任務隊列。
主線程的執行棧執行同步任務,當遇到一些異步任務時,先將異步任務交給對應的線程。
當異步任務滿足條件需要執行時,會將其推入任務隊列,主線程空閑后會順序處理任務隊列中的任務。
(這個執行機制稱為事件循環機制)
備注:對於不同內核的瀏覽器,對應的JS引擎也不同。
IE8的JS引擎是Jscript,IE9開始用Chakra。
FireFox不同版本的JS引擎分別為SpiderMonkey(1.0-3.0)/ TraceMonkey(3.5-3.6)/ JaegerMonkey(4.0)。
Chrome的JS引擎是V8引擎。
事件處理線程
JS引擎線程遇到事件處理程序代碼塊時,會將這個處理程序添加到事件處理線程中。
當事件滿足條件被觸發后,事件處理線程會將該事件添加到前面說的JS執行時維護的任務隊列中,之后就交由JavaScript引擎線程去處理。
定時觸發器線程
處理setTimeout/setInterval代碼的線程就叫定時觸發線程。
當定時器被觸發后,會將需要執行的任務添加到JS的任務隊列,當JS引擎線程空閑的時候,會按順序執行任務隊列中的任務。
(這里牽扯到的一個知識點就是定時器被觸發后不會立即就執行我們編寫的定時任務,必須要等到JS引擎空閑且按順序執行任務隊列中的任務)
異步HTTP請求線程
異步HTTP請求線程主要用於處理XMLHttpRequest請求。
當我們的程序需要向服務器發起請求時,就會交給該線程處理。
當請求得到響應后,如果有需要執行的回調函數,會將回調放入JS的任務隊列,后續在由JS引擎線程處理。
基於前面對這些線程的簡單描述,我畫了一個簡單的圖來解釋這些線程的工作
三.瀏覽器的渲染流程
簡單了解過瀏覽器渲染過程中運行的幾個線程之后,接着我們就需要詳細去了解瀏覽器的渲染流程。
解析構建DOM樹(DOM Tree)
解析意為解釋分析,即將文檔中的代碼轉化為瀏覽器引擎可以理解的規則或者數據結構。
那解析后的結果是代表文檔結構的一個樹,一般將其稱為解析樹或者語法樹。
我們知道一份HTML文檔是由html標簽、CSS規則和JavaScript腳本構成的。
所以在DOM樹的構建過程中,分別會遇到HTML解析、CSS解析和JavaScript腳本解析(或者外部樣式、腳本加載)。
我們以下面這份HTML文檔為例,簡單的梳理一下文檔解析為DOM樹的過程。
<html> <head> <meta chartset="utf-8"> <title>瀏覽器渲染流程</title> <style type="text/css"> .box{ border:1px solid #448aff; } .box span{ width: 100px; height: 100px; border-radius: 50%; } </style> <script type="text/javascript" src="./index.js"></script> </head> <body> <div class="box"> <span class="cicle"></span> </div> </body> </html>
構建DOM樹的過程中,我們需要注意一下幾點:
GUI渲染引擎在處理HTML標簽的同時,也會同步進行CSS規則樹的構建
GUI渲染引擎和JS引擎是互斥的,兩者不能同時進行。當有一個正在運行時,另外一個就需要掛起。
構建CSS規則樹
構建CSS規則樹的過程就是處理CSS樣式的過程,構建CSS規則樹是由CSS解釋器來完成的。
CSS解釋器會將CSS文件解析為一個StyleSheet對象,StyleSheet對象又包含一個或者多個CSSRule對象。
CSSRule對象則包含選擇器對象Selectors和聲明對象Declaration。
那基於前面文檔中的CSS樣式,我們按照規則簡單構建一個CSS規則樹。
CSS解釋器構建CSS規則樹這里我需要總結幾點:
CSS解釋器會將復合的CSS規則拆分成多個聲明對象(可以參考上圖中border的解析)
CSS規則樹的構建和DOM樹的構建是同步進行的
呈現樹構建(Render Tree)
呈現樹基於DOM樹和CSS規則樹構建的,是文檔的可視化表示。
它的作用是為了后續能將元素進行正確的布局和繪制。
在呈現樹的構建過程中,總結了以下幾點:
一些非可視化的元素不會插入到呈現樹中,比如head元素、display為none的元素。
呈現樹構建時會計算每一個可視化元素的樣式屬性。
樣式屬性計算就是為每一個元素查找匹配的CSS規則,這個就是根據我們寫的CSS選擇器進行匹配的。
布局(Layout)
呈現樹構建只是基於DOM樹和CSS規則為元素匹配出了對應的樣式屬性,但是並不會計算出元素在屏幕上從位置和大小。
所以接下來就需要計算元素的位置和大小,我們將其稱為布局。
布局階段會遍歷呈現樹,根據呈現樹中每一個節點的信息(寬、高等樣式屬性)准確計算出每一個節點在頁面上的位置和大小。
繪制(Paint)
繪制階段,瀏覽器引擎會根據呈現樹以及布局計算出來的元素位置和大小,將元素顯示到屏幕上。
四.總結
到此,瀏覽器中的渲染機制就梳理完成了,基本都是概念性的內容,因此我們在對渲染流程進行一個總結:
解析構建DOM樹
構建CSS規則樹
根據DOM樹和CSS規則樹構建呈現樹
根據呈現樹進行布局
根據呈現樹和布局計算出來的位置和大小將元素繪制到瀏覽器中
除了渲染流程,我們還需要關注前面每個過程中需要關注的點。
備注:
在這里我們需要理解GUI渲染引擎和JS引擎的互斥關系。
我們都知道JS代碼是可以操作DOM和CSS樣式的。
假如GUI渲染引擎一直持續不斷的構建DOM樹、規則樹、呈現樹以及后續的布局和繪制。
那么我們JS操作樣式和DOM的邏輯顯然不會在頁面上生效。
五.重排和重繪
在最后呢,我們在補充一下關於頁面渲染過程中會出現的重排和重繪現象。
先來簡單了解一下這兩個概念。
重繪(repaint):界面中有部分元素的外觀發生了變化,
比如:背景顏色,字體顏色,那么瀏覽器需要將該元素的新屬性重新顯示到瀏覽器上,這個叫做重繪。
重排(reflow):重排也叫重構或者回流。
它指的是界面中有部分元素的尺寸,布局發生了變化,那么就會導致發生變化的這部分呈現樹重新構建,這叫做瀏覽器的重排。
新的呈現樹構建完成后,瀏覽器需要重新布局、繪制這部分發生改變的呈現樹到瀏覽器屏幕上(重排后需要進行重繪)。
前面我們說過,呈現樹是基於DOM樹和CSS規則樹的,在這里瀏覽器會有一個規則叫:CSS阻塞呈現樹。
因為假如我們在DOM樹和CSS規則樹構建的同時去構建呈現樹,那可想而知在后續解析CSS樣式時會發生很多次的重排和重繪。
關於重排和重繪就簡單的了解到這里,這兩個現象是不可避免發生的,也不可避免的會造成頁面性能上從損失。
因此后續會有文章詳細解釋瀏覽器的重排和重繪,並且給出優化建議。
作者:小土豆
掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d
微信公眾號:不知名寶藏程序媛(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)
碼字不易,點贊鼓勵喲~