原文連接javascript
在各個瀏覽器廠商你追我趕的形勢下,截止今日,產生了不少不一樣的瀏覽器,各個瀏覽器本質大同小異,核心部分基本類似,由渲染引擎和 JS 引擎組成。當你在訪問網站頁面的時候,瀏覽器作了不少事情,從發送請求,到解析 HTML 源碼,構建渲染樹,最後將內容像素繪製到設備屏幕上,一步步完成用戶最終須要的場景。然而,在瀏覽器完成整個渲染的過程當中,最爲核心的就是「渲染引擎」。如下分別列出一些主流瀏覽器的渲染引擎:php
chrome - webkitcss
safari - webkithtml
opera - webkit(早期是 presto)java
firefox - geckogit
ie - tridentgithub
結合瀏覽器渲染原理,來剖析如下代碼渲染構建過程:web
1.HTML 源碼:chrome
<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> <title>browser rendering</title> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> </body> </html>
2.CSS 源碼:shell
body { font-size: 16px } p { font-weight: bold } span { color: red } p span { display: none } img { float: right }
3.該圖爲以上源代碼構建樹:
瀏覽器渲染頁面整個過程描述:
首先,解析 HTML Source,構建 DOM Tree;
同時,解析 CSS Style,構建 CSSOM Tree;
而後,組合 DOM Tree 與 CSSOM Tree,去除不可見元素,構建 Render Tree;
再執行 Reflow,根據 Render Tree 計算每一個可見元素的佈局(幾何屬性);
最後,執行 Repaint,經過繪製流程,將每一個像素渲染到屏幕上。
注意:
Render Tree 只包含渲染網頁所須要的節點;
Reflow 過程是佈局計算每一個對象的精確位置和大小;
Repaint 過程則是將 Render Tree 的每一個像素渲染到屏幕上。
咱們都知道,網頁是須要掛載在客戶端的瀏覽器上運行,可是隨着互聯網的快速發展,爲保證用戶訪問應用的流暢性,每每對客戶端的設備配置要求較高。然而,對於用戶的設備,咱們是沒法控制;所以,做爲一名開發者,就須要找到除了用戶設備配置以外致使訪問不流暢的問題,下面就從渲染流程中找到影響性能的問題。
瀏覽器初始化渲染時會執行一次 Reflow 過程,這個過程主要是用來肯定頁面上每一個元素在屏幕上的幾何位置屬性。可是,每執行一次 Reflow 會須要花費大量的時間,耗費大量的設備資源,因此儘可能避免執行 Reflow 過程。同時,執行完 Reflow 都會伴隨着一次 Repaint 過程,這個過程也會耗費大量的計算機資源,嚴重影響頁面的渲染性能。因此,在瀏覽器渲染階段,主要影響頁面渲染的階段是 Reflow 和 Repaint 過程,所以在編寫代碼時應該儘可能避免 Reflow 和 Repaint 過程的執行,如:避免在 JS 代碼裏直接改變元素的幾何屬性。
reflow 在渲染過程當中稱爲迴流,發生在 Render Tree 階段,它主要是用來肯定每一個元素在屏幕上的幾何屬性,須要大量計算每一個元素的位置。在代碼裏每改變一個元素的幾何屬性,均會發生一次迴流過程。
repaint 在渲染過程當中稱爲重繪,發生在 reflow 過程以後,當元素的幾何屬性肯定以後便要開始將元素繪製在屏幕上展現。repaint 執行過程就是將元素的顏色、背景等屬性繪製出來。在代碼裏沒改變一次元素的顏色等屬性時均會對相關元素執行一次重繪。
1.改變元素 font-size:
document.getElementsByTagName('body')[0].style.fontSize = '20px'; // reflow,repaint
2.改變元素盒模型margin、border、padding、width:
let styles = document.getElementsByTagName('body')[0].style; styles.margin = '40px'; // reflow,repaint styles.border = '40px solid #f00'; // reflow,repaint styles.padding = '40px'; // reflow,repaint styles.width = '300px'; // reflow,repaint
3.改變元素顏色、背景色屬性:
let styles = document.getElementsByTagName('body')[0].style; styles.color = '#fff'; // repaint styles.backgroundColor = '#f00'; // repaint
4.特殊:offset、scroll、client*、getComputedStyle、currentStyle:
因爲瀏覽器在處理批量修改頁面元素樣式時,會將批量操做緩存起來,而後再作一次 reflow 過程(異步 reflow),避免每次操做都執行 reflow 消耗資源。可是若是在某個操做以後立馬調用了以上執行屬性,爲了等夠獲得最新的樣式,會檢查緩存的操做,是否須要 reflow,這樣就 flush 出最新的樣式。
1.減小 JS 逐行修改元素樣式:
let body = document.getElementsByTagName('body')[0]; body.className += ' class-name';
2.離線處理 DOM 操做:
經過 documentFragment 集中處理臨時操做;
克隆節點進行操做,而後進行原節點替換;
使用 display:none; 進行批量操做。
3.減小樣式的從新計算,即減小 offset、scroll、client*、getComputedStyle、currentStyle 的使用,由於每次調用都會刷新操做緩衝區,執行 reflow & repaint。