JavaScript 如何工做:渲染引擎和性能優化技巧

翻譯自:How JavaScript works: the rendering engine and tips to optimize its performancejavascript

這是探索 JavaScript 及其構建組件專題系列的第 11 篇。在識別和描述核心元素的過程當中,咱們分享了在構建 SessionStack 時使用的一些經驗法則。SessionStack 是一個須要魯棒且高性能的 JavaScript 應用程序,它幫助用戶實時查看和重現它們 Web 應用程序的缺陷。css

當構建 Web 應用程序時,你不僅是編寫獨立運行的 JavaScript 代碼片斷。你編寫的 JavaScript 須要與環境進行交互。理解環境是如何工做的以及它是由什麼組成的,你就可以構建更好的應用程序,而且能更好地處理應用程序發佈後纔會顯現的潛在問題。html

那麼,讓咱們看看瀏覽器的主要組件有哪些:html5

  • 用戶界面:包括地址欄、後退和前進按鈕、書籤菜單等。實際上,它包括了瀏覽器中顯示的絕大部分,除了你看到的網頁自己的那個窗口。
  • 瀏覽器引擎:它處理用戶界面和渲染引擎之間的交互。
  • 渲染引擎:它負責顯示網頁。渲染引擎解析 HTML 和 CSS,並在屏幕上顯示解析的內容。
  • 網絡層:諸如 XHR 請求之類的網絡調用,經過對不一樣平臺的不一樣的實現來完成,這些實現位於一個平臺無關的接口以後。咱們在本系列的上一篇文章中更詳細地討論了網絡層。
  • UI 後端:它用於繪製核心組件(widget),例如複選框和窗口。這個後端暴露了一個平臺無關的通用接口。它使用下層的操做系統提供的 UI 方法。
  • JavaScript 引擎:咱們在上一篇文章中詳細介紹了這一主題。基本上,這是 JavaScript 執行的地方。
  • 數據持久化層:你的應用可能須要在本地存儲全部數據。其支持的存儲機制包括 localStorageindexDBWebSQLFileSystem

在這篇文章中,咱們將關注渲染引擎,由於它負責處理 HTML 和 CSS 的解析和可視化,這是大多數 JavaScript 應用程序不斷與之交互的地方。java

 

渲染引擎概述

渲染引擎的主要職責是在瀏覽器屏幕上顯示所請求的頁面。node

渲染引擎能夠顯示 HTML / XML 文檔和圖像。若是你使用其餘插件,它還能夠顯示不一樣類型的文檔,例如 PDF。git

 

不一樣的渲染引擎

與 JavaScript 引擎相似,不一樣的瀏覽器也使用不一樣的渲染引擎。常見的有這些:github

  • Gecko — Firefox
  • WebKit — Safari
  • Blink — Chrome,Opera (版本 15 以後)

 

渲染的過程

渲染引擎從網絡層接收所請求文檔的內容。web

構建 DOM 樹

渲染引擎的第一步是解析 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 

CSSOM 指 CSS 對象模型。當瀏覽器構建頁面的 DOM 時,它在 head 中遇到了一個引用外部 theme.css 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 標籤中的任何文本的字體大小均爲 16 像素,而且爲紅色。這些樣式是從 body 元素繼承而來的。 若是一個 span 元素是一個 p 元素的子元素,那麼它的內容就不會被顯示,由於它被應用了更具體的樣式(display: none)。

另外請注意,上面的樹不是完整的 CSSOM 樹,只顯示了咱們決定在樣式表中重寫的樣式。每一個瀏覽器都提供了一組默認的樣式,也稱爲 「用戶代理樣式」——這是咱們在未明確指定任何樣式時看到的樣式。咱們的樣式會覆蓋這些默認值。

 

構建渲染樹

HTML 中的視圖指令與 CSSOM 樹中的樣式數據結合在一塊兒用來建立渲染樹

你可能會問什麼是渲染樹。渲染樹是一顆由可視化元素以它們在屏幕上顯示的順序而構成的樹型結構。它是 HTML 和相應的 CSS 的可視化表示。此樹的目的是爲了以正確的順序繪製內容。

渲染樹中的節點被稱爲 Webkit 中的渲染器或渲染對象。

這就是上述 DOM 和 CSSOM 樹的渲染器樹的樣子:

爲了構建渲染樹,瀏覽器大體作了以下工做:

  • 從 DOM 樹的根開始,瀏覽器遍歷每一個可見節點。某些節點是不可見的(例如 script、meta 等),而且因爲它們不須要渲染而被忽略。一些經過 CSS 隱藏的節點也從渲染樹中省略。例如 span 節點 —— 在上面的例子中,它並不存在於渲染樹中,由於咱們明確地其上設置了 display: none 屬性。
  • 對於每一個可見節點,瀏覽器找到適當的 CSSOM 規則並應用它們。
  • 瀏覽器輸出帶有內容及其計算出的樣式的可見節點

你能夠在這裏查看 RenderObject 的源代碼(在 WebKit 中):https://github.com/WebKit/webkit/blob/fde57e46b1f8d7dde4b2006aaf7ebe5a09a6984b/Source/WebCore/rendering/RenderObject.h

咱們來看看這個類的一些核心內容:

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 使用基於流的佈局模型,這意味着大部分時間內它能夠在一次遍歷中(single pass)計算出佈局。座標系是相對於根渲染器的,使用左上原點座標。

佈局是一個遞歸過程 —— 它從根渲染器開始,對應於 HTML 文檔的 <html> 元素,經過部分或整個渲染器的層次結構遞歸地爲每一個須要佈局的渲染器計算佈局信息。

根渲染器的位置是 0,0,而且其尺寸爲瀏覽器窗口(也稱爲視口)的可見部分的尺寸。

開始佈局過程意味着給出每一個節點它應該出如今屏幕上的確切座標。

 

繪製渲染樹

在這個階段,瀏覽器遍歷渲染器樹,調用渲染器的 paint() 方法在屏幕上顯示內容。

繪圖能夠是全局的或增量式的(與佈局相似):

  • 全局 —— 整棵樹被重畫
  • 增量式 —— 只有一些渲染器以不影響整個樹的方式進行變動。渲染器在屏幕上標記其矩形區域無效,這會致使操做系統將其視爲須要重繪並生成 paint 事件的區域。操做系統經過將幾個區域合併爲一個區域的智能方式來完成繪圖。

通常來講,瞭解繪圖是一個漸進的過程是很重要的。爲了更好的用戶體驗,渲染引擎會嘗試儘快在屏幕上顯示內容。它不會等到全部的 HTML 被分析完畢纔開始構建和佈置渲染樹。一小部份內容先被解析並顯示,同時一邊從網絡獲取剩下的內容一邊漸進地渲染。

處理腳本和樣式表的順序

當解析器到達 <script> 標籤時,腳本將被當即解析並執行。文檔解析將會被暫停,直到腳本執行完畢。這意味着該過程是同步的。

若是腳本是外部的,那麼它首先必須從網絡獲取(也是同步的)。全部解析都會中止,直到網絡請求完成。

HTML5 添加了一個選項,能夠將腳本標記爲異步,此時腳本被其餘線程解析和執行。

 

優化渲染性能

若是你想優化你的應用,那麼你須要關注五個主要方面。這些是您能夠控制的地方:

  1. JavaScript —— 在以前的文章中,咱們介紹了關於編寫高性能代碼的主題,這些代碼不會阻塞 UI,而且內存效率高等等。當涉及渲染時,咱們須要考慮 JavaScript 代碼與頁面上 DOM 元素交互的方式。JavaScript 能夠在 UI 中產生大量的更新,尤爲是在 SPA 中。
  2. 樣式計算 —— 這是基於匹配選擇器肯定哪一個 CSS 規則適用於哪一個元素的過程。一旦定義了規則,就會應用這些規則,並計算出每一個元素的最終樣式。
  3. 佈局 —— 一旦瀏覽器知道哪些規則適用於元素,就能夠開始計算後者佔用的空間以及它在瀏覽器屏幕上的位置。Web 的佈局模型定義了一個元素能夠影響其餘元素。例如,<body> 的寬度會影響子元素的寬度等等。這一切都意味着佈局過程是計算密集型的。該繪圖是在多個圖層完成的。
  4. 繪圖 —— 這裏開始填充實際的像素。該過程包括繪製文本、顏色、圖像、邊框、陰影等 —— 每一個元素的每一個視覺部分。
  5. 合成 —— 因爲頁面部件被劃分爲多層,所以須要按照正確的順序將其繪製到屏幕上,以便正確地渲染頁面。這很是重要,特別是對於重疊元素來講。

 

優化你的 JavaScript

JavaScript 常常觸發瀏覽器中的視覺變化,構建 SPA 時更是如此。

如下是關於能夠優化 JavaScript 哪些部分來改善渲染性能的一些小提示:

  • 避免使用 setTimeoutsetInterval 進行視圖更新。這些將在幀中某個不肯定的時間點上調用 callback,可能在最後。咱們想要作的是在幀開始時觸發視覺變化而不是錯過它。
  • 將長時間運行的 JavaScript 計算任務移到 Web Workers 上,像咱們以前討論過的那樣
  • 使用微任務在多個幀中變動 DOM。這是爲了處理在 Web Worker 中的任務須要訪問 DOM,而 Web Worker 又不容許訪問 DOM 的狀況。就是說你能夠將一個大任務分解爲小任務,並根據任務的性質在 requestAnimationFramesetTimeoutsetInterval 中運行它們。

 

優化你的 CSS

經過添加和刪除元素、更改屬性等來修改 DOM 會致使瀏覽器從新計算元素樣式,而且在不少狀況下還會從新佈局整個頁面或至少其中的一部分。

要優化渲染性能,請考慮如下方法:

  • 減小選擇器的複雜性。相對於構建樣式自己的工做,複雜的選擇器可能會讓計算元素樣式所需的時間增長 50%。
  • 減小必須計算樣式的元素的數量。本質上,直接對幾個元素進行樣式更改,而不是使整個頁面無效。

 

優化佈局

佈局的從新計算會對瀏覽器形成很大壓力。請考慮下面的優化:

  • 儘量減小布局的數量。當你更改樣式時,瀏覽器將檢查是否須要從新計算佈局。對屬性的更改,如寬度、高度、左、上和其餘與幾何有關的屬性,都須要從新佈局。因此,儘可能避免改變它們。
  • 儘可能使用 flexbox 而不是老的佈局模型。它運行速度更快,可爲你的應用程序創造巨大的性能優點。
  • 避免強制同步佈局。須要注意的是,在 JavaScript 運行時,前一幀中的全部舊佈局值都是已知的而且能夠查詢。若是你查詢 box.offsetHeight 是沒問題的。 可是,若是你在查詢元素以前更改了元素的樣式(例如,動態向元素添加一些 CSS 類),瀏覽器必須先應用樣式更改並執行佈局過程。這可能很是耗時且耗費資源,所以請儘量避免。

 

優化繪圖

這一般是全部任務中運行時間最長的,所以儘量避免這種狀況很是重要。 如下是咱們能夠作的事情:

  • 除了變換(transform)和透明度以外,改變其餘任何屬性都會觸發從新繪圖,請謹慎使用。
  • 若是觸發了佈局,那也會觸發繪圖,由於更改佈局會致使元素的視覺效果也改變。
  • 經過圖層提高和動畫編排來減小重繪區域。

渲染是 SessionStack 運行的重點之一。當用戶瀏覽你的 web 應用遇到問題時,SessionStack 必須將這些遇到的問題重建成一個視頻。爲了作到這點,SessionStack 僅利用咱們的庫收集到數據:用戶事件、DOM 更改、網絡請求、異常和調試消息等。咱們的播放器通過高度優化,可以按順序正確呈現和使用全部收集到的數據,從視覺和技術兩方面爲你提供用戶在瀏覽器中發生的一切的像素級完美模擬。

若是你想試試看,這裏能夠免費嘗試 SessionStack

 

資源

本文來自網易雲社區,經做者徐子航受權發佈。

原文地址:JavaScript 如何工做:渲染引擎和性能優化技巧

更多網易研發、產品、運營經驗分享請訪問網易雲社區

相關文章
相關標籤/搜索