JavaScript工做原理(十):渲染引擎和優化性能的技巧

到目前爲止,在咱們以前的「JavaScript工做原理」系列文章中,咱們一直關注JavaScript做爲一種語言,其功能,它如何在瀏覽器中執行,如何優化等等。css

可是,當您構建Web應用程序時,您不僅是編寫獨立運行的獨立JavaScript代碼。您編寫的JavaScript與環境進行交互。瞭解這種環境,它是如何工做的以及它的組成是什麼,將使您可以構建更好的應用程序,並對應用程序發佈後可能出現的潛在問題作好充分準備。
圖片描述html

那麼,讓咱們看看瀏覽器的主要組件是什麼:node

  • 用戶界面:這包括地址欄,後退和前進按鈕,書籤菜單等。實質上,這是瀏覽器顯示的每一個部分,除了您看到網頁自己的窗口。
  • 瀏覽器引擎:它處理用戶界面和渲染引擎之間的交互
  • 渲染引擎:它負責顯示網頁。渲染引擎解析HTML和CSS,並在屏幕上顯示解析的內容。
  • 網絡:這些是網絡調用,例如XHR請求,經過對不一樣平臺使用不一樣的實現來實現,這些平臺位於獨立於平臺的接口以後。在本系列的前一篇文章中,咱們更詳細地討論了網絡層。
  • UI後端:用於繪製核心小部件,如複選框和窗口。這個後端公開了一個不是平臺特定的通用接口。它使用下面的操做系統UI方法。
  • JavaScript引擎:咱們在該系列的前一篇文章中詳細介紹了這一點。基本上,這是JavaScript執行的地方。
  • 數據持久性:您的應用可能須要在本地存儲全部數據。支持的存儲機制類型包括localStorage,indexDB,WebSQL和FileSystem。

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

渲染引擎的概述

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

渲染引擎能夠顯示HTML和XML文檔和圖像。若是您使用額外的插件,引擎還能夠顯示不一樣類型的文檔,如PDF。web

渲染引擎

與JavaScript引擎相似,不一樣的瀏覽器也使用不一樣的渲染引擎。這些是一些流行的:後端

  • Gecko - 火狐
  • WebKit - Safari
  • Blink - Chrome,Opera(從15版開始)

渲染的過程

渲染引擎從網絡層接收所請求文檔的內容。
圖片描述瀏覽器

構建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時,它在引用外部theme.css CSS樣式表的head部分遇到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元素的子元素,則因爲正在應用更具體的樣式,所以不會顯示其內容。

另外請注意,上面的樹不是完整的CSSOM樹,只顯示了咱們決定在樣式表中重寫的樣式。每一個瀏覽器都提供了一組默認的樣式,也稱爲「user agent styles」 - 這是咱們在沒有明確提供任何樣式時看到的。咱們的樣式簡單地覆蓋這些默認值。

構建渲染樹

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

你可能會問什麼是渲染樹?這是按照它們在屏幕上顯示的順序構建的視覺元素樹。它是HTML和相應的CSS的可視化表示。此樹的目的是爲了以正確的順序繪製內容。

Webkit中,渲染樹中的每一個節點都被稱爲的渲染器或渲染對象。

這就是上述DOM和CSSOM樹的渲染器樹的外觀:
圖片描述

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

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

你能夠在這裏看看RenderObject的源代碼(在WebKit中):https://github.com/WebKit/web...

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

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()方法以在屏幕上顯示內容。

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

  • 全局 - 整個樹被從新繪製。
  • 增量 - 只有一些渲染器以不影響整個樹的方式進行更改。渲染器使其矩形在屏幕上無效。這會致使操做系統將其視爲須要重繪和生成繪畫事件的區域。操做系統經過將幾個區域合併爲一個智能方式來完成。

通常來講,瞭解繪畫是一個漸進的過程是很重要的。爲了更好的用戶體驗,渲染引擎會嘗試儘快在屏幕上顯示內容。它不會等到全部的HTML被解析,纔開始構建和佈置渲染樹。內容的部份內容將被解析並顯示,而該過程繼續保持來自網絡的其他內容項目。

處理腳本和樣式表的順序

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

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

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

優化渲染性能

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

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

優化您的JavaScript

JavaScript常常觸發瀏覽器中的視覺變化。當創建一個SPA時更是如此。

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

  • 爲視覺更新避免setTimeout或setInterval。這些將在框架中的某個點調用回調,最後可能會發生。咱們想要作的就是在畫面開始時觸發視覺變化,不要錯過它。
  • 如前所述,將長時間運行的JavaScript計算移至Web Workers。
  • 使用微任務在多個框架中引入DOM更改。這是爲了防止任務須要訪問DOM,Web Worker不能訪問這些任務。這基本上意味着你將一個大任務分解成更小的任務,並根據任務的性質在requestAnimationFrame,setTimeout,setInterval中運行它們。

優化你的CSS

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

要優化渲染,請考慮如下幾點:

  • 減小選擇器的複雜性。選擇器的複雜度可能比計算元素樣式所需的時間多50%,而構建樣式自己的其他工做則須要花費超過50%的時間。
  • 減小style計算必須發生的元素數量。實質上,直接對幾個元素進行樣式更改,而不是使整個頁面失效。

優化佈局

瀏覽器的佈局從新計算可能很是繁重。考慮如下優化:

  • 儘量減小布局的數量。當您更改樣式時,瀏覽器會檢查是否有任何更改要求從新計算佈局。對屬性(如寬度,高度,左側,頂部以及一般與幾何相關的屬性)的更改須要佈局。因此,儘可能避免改變它們。
  • 儘量在較早的佈局模型上使用Flexbox。它運行速度更快,可爲您的應用程序創造巨大的性能優點。
  • 避免強制同步佈局。須要記住的是,在JavaScript運行時,前一幀中的全部舊佈局值都是已知的,可供您查詢。若是你訪問box.offsetHeight它不會是一個問題。可是,若是您在訪問該框以前更改了框的樣式(例如,經過向元素動態添加一些CSS類),瀏覽器必須先應用樣式更改並運行佈局。這可能很是耗時且耗費資源,所以請儘量避免。

優化paint

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

  • 更改除變換或不透明度之外的任何屬性都會觸發繪畫。謹慎使用它。
  • 若是觸發layout,則還會觸發paint,由於更改幾何圖形會致使元素的視覺更改。
  • 經過圖層提高和動畫編排來減小繪畫區域。
相關文章
相關標籤/搜索