好久沒有翻譯文章了,最近看到一篇不錯的文章,恰好安排上。javascript
原文地址:How JavaScript works: the rendering engine and tips to optimize its performancecss
這是探討 JavaScript
系列文章的第 11 篇,專門探討 JavaScript
及其構建組件。識別和描述核心元素的過程當中,咱們還分享了一些在構建 SessionStack
時使用的經驗法則。SessionStack
是一個須要強大且高性能的 JavaScript
應用程序,以幫助用戶實時發現並重現他們的 Web 應用程序缺陷。html
若是你想閱讀系列的其餘文章,能夠看下面的連接:html5
到目前爲止,在咱們以前的 "JavaScript的工做原理" 系列博客文章中,咱們一直專一於JavaScript做爲一種語言,其功能,如何在瀏覽器中執行,如何對其進行優化等。java
可是,在構建 Web 應用程序時,不只僅只是會編寫獨立運行的 JavaScript 代碼。您編寫的 JavaScript 代碼須要和環境進行交互。瞭解此環境,和其工做方式以及它的組成將使您可以構建更好的 web 應用程序,並能爲後續應用程序發佈後可能出現的潛在問題作好充分的準備。 node
那麼,咱們來看看瀏覽器主要由哪些部分組成:git
本文中,咱們將重點介紹渲染引擎,由於它處理 HTML 和 CSS 的解析和可視化,並且大多數 JavaScript 應用程序會一直與之交互。github
渲染引擎主要負責在瀏覽器屏幕上顯示請求的頁面。web
渲染引擎能夠顯示 HTML 和 XML 文件和圖像。若是你使用其餘的插件,該引擎還能顯示不一樣類型的文檔,如 PDF。編程
與 JavaScript 引擎相似,不一樣的瀏覽器也使用不一樣的渲染引擎。下面是一些流行的渲染引擎:
渲染引擎從網絡層接收所請求文檔的內容。
渲染引擎的第一步是解析 HTML 文檔,並將解析的元素轉換爲DOM 樹 中的實際 DOM 節點。
假設你輸入瞭如下文本:
<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>
複製代碼
該 HTML 文檔的 DOM 樹看起來像下面這樣:
基本上,每一個元素都表示爲其直接所包含的全部元素的父節點,而且這是遞歸進行的。
CSSOM 指的是 CSS 對象模型。當瀏覽器構建頁面的 DOM 時,在 head
部分,遇到引用外部層疊樣式表 theme.css
的 link
標籤時,預計可能須要這個資源來渲染頁面,它就當即爲此發送了一個請求。 假設 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 標籤所包含的全部文本,字體大小都是 16px 而且顏色爲紅色。這些樣式是從 body 元素繼承的。 若是 span 元素是 p 標籤的子元素,則會由於設置了更具體的樣式,而不會顯示其內容。
另外,請注意,上面的樹不是完整的 CSSOM 樹,它只顯示了樣式表中咱們決定重寫的樣式。每一個瀏覽器都提供了一組默認的樣式,也稱爲 用戶代理樣式 — 也就是咱們沒有明確提供任何樣式時會看到的樣式。 咱們的樣式只是覆蓋了這些默認樣式。
HTML 中的視覺指令與 CSSOM 樹中的樣式數據相結合,用於建立渲染樹。
你可能會問什麼是渲染樹?這是一個可視化元素樹,按照它們在屏幕上顯示的順序構建。它是 HTML 和相應的 CSS 的可視化表示。此樹的目的是使內容可以按正確的順序進行繪製。
渲染樹中的每一個節點稱爲渲染器或WebKit中的渲染對象。
這裏是上面的 DOM 和 CSSOM 樹的渲染器樹的樣子:
要構造渲染樹,瀏覽器大體執行如下操做:
你能夠在這裏查看 RenderObject 的源代碼(在webkit中):github.com/WebKit/webk…
讓咱們來看看這個類的核心內容:
class RenderObject : public CachedImageClient {
// Repaint the entire object. Called when, e.g., the color of a border changes, or when a border
// style changes.
Node* node() const { ... }
RenderStyle* style; // the computed style
const RenderStyle& style() const;
...
}
複製代碼
每一個渲染器表明一個一般與某節點的 CSS 盒子相對應的矩形區域。它包含了一些幾何信息,如寬,高和位置。
當渲染器被建立並添加到樹中時,它是沒有位置和大小的。而計算這些值就稱爲佈局。
HTML 使用基於流的佈局模型,這意味着它大多數狀況下能夠一次性計算出幾何結構。座標系是相對於根渲染器的,使用頂部和左側座標。
佈局是一個遞歸的過程 — 它從對應於 HTML 文檔 <html>
元素的根渲染器開始。佈局經過部分或整個渲染器的層級繼續遞歸,爲每一個依賴它的渲染器計算幾何信息。
根渲染器的位置爲0,0
,其尺寸與瀏覽器窗口(即視區)的可見部分大小相同。
佈局過程的開始意味着給每一個節點賦予應該在屏幕中顯示的精確座標。
在此階段,將遍歷渲染器樹,並調用渲染器的 paint()
方法在屏幕上顯示內容。
繪製能夠是全局的或遞增的(相似於佈局):
paint
事件。操做系統經過一種將多個區域合併爲一個區域的智能方式來實現這一點。一般來講,瞭解繪製是一個漸進的過程是很是重要的。爲了更好的用戶體驗,渲染引擎會盡量快地將內容顯示在屏幕上。它不會等到全部的 HTML 都被解析後纔開始構建和繪製渲染樹。部份內容將被解析並顯示,同時該過程會繼續處理來自網絡的其他內容項。
當解析器到達 <script>
標籤時,將當即解析並執行腳本。在腳本執行以前,將中止對文檔的解析,這意味着該過程是同步的。
若是腳本是外部的,那麼首先必須從網絡中獲取它(也是同步的)。在獲取完成以前,全部解析都將中止。
HTML5 添加了一個選項,將腳本標記爲異步腳本,以便由其餘線程解析和執行。
若是你想優化你的應用程序,你須要關注五個主要方面。如下是你能夠控制的方面:
JavaScript 常常觸發瀏覽器中的視覺變化。尤爲是構建單頁應用時。
下面是一些關於你能夠優化 JavaScript 的哪些部分來改進渲染的建議:
setTimeout
或 setInterval
進行視覺更新。它們將在幀的某個不肯定時刻調用 「callback」,可能在幀結束時執行。咱們要作的是在幀開始時觸發視覺變化而不要錯過它。requestAnimationFrame
,setTimeout
,setInterval
中運行它們。經過添加和刪除元素,修改屬性等更改 DOM 將使瀏覽器從新計算元素樣式,在許多狀況下,還會從新計算整個頁面或至少部分頁面的佈局。
要優化渲染,請考慮如下內容:
佈局的從新計算對於瀏覽器來講可能很是繁重。能夠考慮如下方面的優化:
width,height,left,top
等屬性以及一般與幾何圖形相關的屬性的更改,都須要佈局。因此,儘可能避免改變這些屬性。flexbox
。它渲染得更快,能夠爲你的應用程序創造巨大的性能優點。box.offthesight
是沒有問題的。可是,若是在獲取前更改了盒子的樣式(例如,給元素動態添加一些 CSS 類),瀏覽器必須先去更改樣式,而後再進行佈局。這可能很是耗費時間和資源,所以要儘量避免。繪製一般是全部任務中運行時間最長的,所以儘量避免繪製是很是重要的。咱們能夠這樣作:
渲染是 SessionStack 正常運轉的一個重要方面。SessionStack 必須將用戶在瀏覽 Web 應用程序時遇到問題時發生的全部事情從新建立成一個視頻。爲此,SessionStack 僅利用咱們的庫收集的數據:用戶事件、DOM更改、網絡請求、異常、調試消息等。咱們的播放器通過高度優化,可以正確地渲染和使用全部收集的數據,以便不管從視覺上仍是技術上爲用戶的瀏覽器和瀏覽器中發生的一切提供一個完美像素模擬。
若是你想 試一試 SessionStack,有一個免費的計劃。