摘要: 理解瀏覽器渲染。
Fundebug經授權轉載,版權歸原作者所有。
這是專門探索 JavaScript 及其所構建的組件的系列文章的第11篇。
如果你錯過了前面的章節,可以在這裏找到它們:
當你構建 Web 應用程序時,你不只是編寫單獨運行的 JavaScript 代碼,你編寫的 JavaScript 正在與環境進行交互。瞭解這種環境,它的工作原理以及它的組,這些有助於你夠構建更好的應用程序,併爲應用程序發佈後可能出現的潛在問題做好充分準備。
瀏覽器的主要組件包括:
在這篇文章中,將重點討論渲染引擎,因爲它處理 HTML 和 CSS 的解析和可視化,這是大多數 JavaScript 應用程序經常與之交互的東西。
渲染引擎的職責就是渲染,即在瀏覽器窗口中顯示所請求的內容。
渲染引擎可以顯示 HTML 和 XML 文檔和圖像。如果使用其他插件,渲染引擎還可以顯示不同類型的文檔,如 PDF。
與 JavaScript 引擎類似,不同的瀏覽器也使用不同的渲染引擎。以下是一些最受歡迎的:
Firefox、Chrome 和 Safari 是基於兩種渲染引擎構建的,Firefox 使用 Geoko——Mozilla 自主研發的渲染引擎,Safari 和 Chrome 都使用 Webkit。Blink 是 Chrome 基於 WebKit的自主渲染引擎。
渲染引擎從網絡層接收所請求文檔的內容。
解析 HTML 以構建 Dom 樹 -> 構建 Render 樹 -> 佈局 Render 樹 -> 繪製 Render 樹
渲染現引擎的第一步是解析 HTML文檔,並將解析後的元素轉換爲 DOM 樹中的實際 DOM 節點。
假如有如下 Html 結構
<html> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="theme.css"> </head> <body> <p> Hello, <span> friend! </span> </p> <div> <img src="smiley.gif" alt="Smiley face" height="42" width="42"> </div> </body> </html>
對應的 DOM 樹如下:
基本上,每個元素都表示爲所有元素的父節點,這些元素直接包含在元素中。
CSSOM 指的是 CSS 對象模型。 當瀏覽器構建頁面的 DOM 時,它在 head
標籤下如遇到了一個 link
標記且引用了外部 theme.css CSS 樣式表。 瀏覽器預計可能需要該資源來呈現頁面,它會立即發送請求。 假設 theme.css
文件內容如下:
body { font-size: 16px; } p { font-weight: bold; } span { color: red; } p span { display: none; } img { float: right; }
與 HTML一樣,渲染引擎需要將 CSS 轉換成瀏覽器可以使用的東西—— CSSOM。CSSOM 結構如下:
你想知道爲什麼 CSSOM 是一個樹形結構? 在爲頁面上的任何對象計算最終樣式集時,瀏覽器以適用於該節點的最常規規則開始(例如,如果它是 body
元素的子元素,則應用所有 body
樣式),然後遞歸地細化,通過應用更具體的規則來計算樣式。
來看看具體的例子。包含在 body
元素內的 span
標籤中的任何文本的字體大小均爲 16
像素,並且爲紅色。這些樣式是從 body
元素繼承而來的。 如果一個 span
元素是一個 p
元素的子元素,那麼它的內容就不會被顯示,因爲它被應用了更具體的樣式(display: none)。
另請注意,上面的樹不是完整的 CSSOM 樹,只顯示我們決定在樣式表中覆蓋的樣式。 每個瀏覽器都提供一組默認樣式,也稱爲**「user agent stylesheet」**。這是我們在未明確指定任何樣式時看到的樣式,我們的樣式會覆蓋這些默認值。
不同瀏覽器對於相同元素的默認樣式並不一致,這也是爲什麼我們在 CSS 的最開始要寫 *{padding:0;marging:0};
,也就是我們要重置CSS默認樣式的。
CSSOM 樹和 DOM 樹連接在一起形成一個 render tree,渲染樹用來計算可見元素的佈局並且作爲將像素渲染到屏幕上的過程的輸入。
渲染樹中的每個節點在 Webkit 中稱爲渲染器或渲染對象。
收下是上面 DOM 和 CSSOM 樹的渲染器樹的樣子:
爲了構建渲染樹,瀏覽器大致執行以下操作:
display:none
「visibility:hidden」 和 「display:none」 之間的不同,「visibility:hidden」 將元素設置爲不可見,但是同樣在佈局上佔領一定空間(例如,它會被渲染成爲空盒子),但是 「display:none」 的元素是將節點從整個
render tree
中移除,所以不是佈局中的一部分 。
你可以在這裏查看 RenderObject 的源代碼(在 WebKit 中):
https://github.com/WebKit/web…
我們來看看這個類的一些核心內容:
每個渲染器代表一個矩形區域,通常對應於一個節點的 CSS 盒模型。它包含幾何信息,例如寬度、高度和位置。
代碼部署後可能存在的BUG沒法實時知道,事後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具Fundebug。
創建渲染器並將其添加到樹中時,它沒有位置和大小,計算這些值稱爲佈局。
HTML使用基於流的佈局模型,這意味着大多數時間它可以一次性計算幾何圖形。座標系統相對於根渲染器,使用左上原點座標。
佈局是一個遞歸過程 - 它從根渲染器開始,它對應於 HTML 文檔的 <html>
元素。 佈局以遞歸方式繼續通過部件或整個渲染器層次結構,爲每個需要它的渲染器計算幾何信息。
根渲染器的位置爲0,0
,其尺寸與瀏覽器窗口的可見部分(即viewport)的大小相同。開始佈局過程意味着給每個節點在屏幕上應該出現的確切座標。
在此繪製,遍歷渲染器樹並調用渲染器的 paint()
方法以在屏幕上顯示內容。
繪圖可以是全局的或增量式的(與佈局類似):
paint
事件的區域。 操作系統通過將多個區域合併爲一個來智能完成。總的來說,重要的中要理解繪圖是一個漸進的過程。爲了更好的用戶體驗,渲染引擎將盡可能快地在屏幕上顯示內容。它不會等到解析完所有 HTML 後纔開始構建和佈局渲染樹,而是解析和顯示部分內容,同時繼續處理來自網絡的其餘內容項。
當解析器到達 <script>
標記時,將立即解析並執行腳本。文檔的解析將暫停,直到執行腳本爲止。這意味着這個過程是同步的。
如果腳本是外部的,那麼首先必須從網絡中獲取它(也是同步的)。所有解析都停止,直到獲取完成。HTML5 新加了async 或 defer 屬性,將腳本標記爲異步的,以便由不同的線程解析和執行。
如果你想優化自己的應用,則需要關注五個主要方面,這些是你自己可以控制的:
JavaScript 經常觸發瀏覽器中的視覺變化,構建 SPA 時更是如此。
以下是一些優化 JavaScript 渲染技巧:
setTimeout
或 setInterval
進行可視更新。 這些將在幀中的某個點調用 callback
,可能在最後。我們想要做的是在幀開始時觸發視覺變化而不是錯過它。requestAnimationFrame
, setTimeout
, setInterval
中運行它們。通過添加和刪除元素,更改屬性等來修改 DOM 將使瀏覽器重新計算元素樣式,並且在許多情況下,重新計算整個頁面的佈局或至少部分佈局。
要優化渲染,考慮以下事項:
* 減少必須進行樣式計算的元素的數量。本質上,直接對一些元素進行樣式更改,而不是使整個頁面無效。
瀏覽器的佈局重新計算可能非常繁重。 考慮以下優化:
box.offsetHeight
,那就不成問題了。但是,如果你在訪問 box
之前更改了它的樣式(例如,通過動態地向元素添加一些 CSS 類),瀏覽器必須先應用樣式更改並執行佈局過程,這是非常耗時和耗費資源的,所以儘可能避免。這通常是所有任務中運行時間最長的,因此儘可能避免這種情況非常重要。 以下是我們可以做的事情:
原文:
https://blog.sessionstack.com…
Fundebug專注於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了9億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等衆多品牌企業。歡迎大家免費試用!