當咱們打開一個瀏覽器,輸入一個url,按下回車,不用多久,一個漂亮的頁面就呈如今咱們眼前了。其實,在咱們等待頁面打開的這段時間裏,瀏覽器但是作了很多的事情,究竟是什麼事情呢,就咱們一塊兒來看看吧css
首先,咱們須要先了解一下什麼是頁面渲染以及頁面渲染的整個過程~html
一、頁面渲染瀏覽器
簡單地說,頁面渲染就是瀏覽器將 HTML 代碼根據 CSS 定義的規則顯示在瀏覽器窗口中的這個過程服務器
二、渲染過程佈局
1. 用戶輸入網址(假設是個 HTML 頁面,而且是第一次訪問),瀏覽器向服務器發出請求,服務器返回 HTML 文件;性能
2. 瀏覽器開始載入 HTML 代碼,發現 <head> 標籤內有一個 <link> 標籤引用外部 CSS 文件;學習
3. 瀏覽器又發出 CSS 文件的請求,服務器返回這個 CSS 文件;字體
4. 瀏覽器繼續載入 HTML 中 <body> 部分的代碼,而且 CSS 文件已經拿到手了,能夠開始渲染頁面了;優化
5. 瀏覽器在代碼中發現一個 <img> 標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼;動畫
6. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼;
7. 瀏覽器發現了一個包含一行 JavaScript 代碼的 <script> 標籤,趕快運行它;
8. JavaScript 腳本執行了這條語句,它命令瀏覽器隱藏掉代碼中的某個 <div>(style.display=」none」)。杯具啊,忽然就少了這麼一個元素,瀏覽器不得不從新渲染這部分代碼;
9. 終於等到了 </html> 的到來,瀏覽器淚流滿面……
10. 等等,還沒完,用戶點了一下界面中的「換膚」按鈕,JavaScript 讓瀏覽器換了一下 <link> 標籤的 CSS 路徑;
11. 瀏覽器召集了在座的各位 <div><span><ul><li> 們,「大夥兒收拾收拾行李,咱得從新來過……」,瀏覽器向服務器請求了新的CSS文件,從新渲染頁面。
如今你們已經知道什麼是頁面渲染了吧,接下來再介紹兩個概念,reflow和repaint~
三、reflow(迴流)
首先說一下頁面爲何會慢?那是由於瀏覽器要花時間、花精力去渲染,尤爲是當它發現某個部分發生了點變化影響了佈局,須要倒回去從新渲染,內行稱這個回退的過程叫 reflow
下面這張圖很清晰的說明了這個過程
reflow 幾乎是沒法避免的。如今界面上流行的一些效果,好比樹狀目錄的摺疊、展開(實質上是元素的顯 示與隱藏)等,都將引發瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引發了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引發它內部、周圍甚至整個頁面的從新渲 染。一般咱們都沒法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響着。
固然,reflow 問題是能夠優化的,咱們能夠儘可能減小沒必要要的 reflow。好比開頭的例子中的 <img> 圖片載入問題,這其實就是一個能夠避免的 reflow —— 給圖片設置寬度和高度就能夠了。這樣瀏覽器就知道了圖片的佔位面積,在載入圖片前就預留好了位置。
四、repaint(重繪)
另外,有個和 reflow 看上去差很少的術語:repaint,中文叫重繪。若是隻是改變某個元素的背景色、文 字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性,將只會引發瀏覽器 repaint。repaint 的速度明顯快於 reflow(在IE下須要換一下說法,reflow 要比 repaint 更緩慢)。
學習了上面的知識後,相信你們對瀏覽器渲染已經有了一個比較深入的瞭解了吧,接下來讓咱們進行更深刻的學習
五、瀏覽器工做大體流程
從上面這個圖中,咱們能夠看到那麼幾個事:
1)瀏覽器會解析三個東西:
2)解析完成後,瀏覽器引擎會經過 DOM Tree 和 CSS Rule Tree 來構造 Rendering Tree。注意:
3)最後經過調用操做系統 Native GUI 的 API 繪製。
六、DOM解析
HTML 的 DOM Tree 解析以下:
上面這段 HTML 會解析成這樣:
下面是另外一個有 SVG 標籤的狀況:
七、CSS 解析
CSS 的解析大概是下面這個樣子(下面主要說的是 Gecko 也就是 Firefox 的玩法),假設咱們有下面的 HTML 文檔:
因而 DOM Tree 是這個樣子:
而後咱們的 CSS 文檔是這樣的:
因而咱們的 CSS Rule Tree 會是這個樣子:
注意,圖中的第 4 條規則出現了兩次,一次是獨立的,一次是在規則 3 的子結點。因此,咱們能夠知道,創建 CSS Rule Tree 是須要比照着 DOM Tree 來的。CSS 匹配 DOM Tree 主要是從右到左解析 CSS 的 Selector,好多人覺得這個事會比較快,其實並不必定。關鍵還看咱們的 CSS 的 Selector 怎麼寫了。
注意:CSS 匹配 HTML 元素是一個至關複雜和有性能問題的事情。因此,你就會在N多地方看到不少人都告訴你,DOM 樹要小,CSS 儘可能用 id 和 class,千萬不要過渡層疊下去,……
經過這兩個樹,咱們能夠獲得一個叫 Style Context Tree,也就是下面這樣(把 CSS Rule 結點 Attach 到 DOM Tree 上):
因此,Firefox 基本上來講是經過 CSS 解析生成 CSS Rule Tree,而後,經過比對 DOM 生成 Style Context Tree,而後 Firefox 經過把 Style Context Tree 和其 Render Tree(Frame Tree)關聯上,就完成了。注意:Render Tree 會把一些不可見的結點去除掉。而 Firefox 中所謂的 Frame 就是一個 DOM 結點,不要被其名字所迷惑了。
八、重說渲染
渲染的流程基本上以下(黃色的四個步驟):
(1)計算 CSS 樣式
(2)構建 Render Tree
(3)Layout – 定位座標和大小,是否換行,各類 position, overflow, z-index 屬性 ……
(4)正式開畫
注意:上圖流程中有不少鏈接線,這表示了 Javascript 動態修改了 DOM 屬性或是 CSS 屬性會致使從新 Layout,有些改變不會,就是那些指到天上的箭頭,好比,修改後的 CSS rule 沒有被匹配到,等。
這裏從新說下兩個概念,一個是 Reflow,另外一個是 Repaint。這兩個不是一回事。
因此,下面這些動做有很大可能會是成本比較高的。
注:display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,由於沒有發現位置變化。
九、一些優化建議
(1)不要一條一條地修改 DOM 的樣式。與其這樣,還不如預先定義好 css 的 class,而後修改 DOM 的 className
(2)把 DOM 離線後修改。如:
(3)不要把 DOM 結點的屬性值放在一個循環裏當成循環裏的變量。否則這會致使大量地讀寫這個結點的屬性。
(4)儘量的修改層級比較低的 DOM。固然,改變層級比較底的 DOM 有可能會形成大面積的 reflow,可是也可能影響範圍很小。
(5)爲動畫的 HTML 元件使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是不會 reflow 的。
(6)千萬不要使用 table 佈局。由於可能很小的一個小改動會形成整個 table 的從新佈局。