瀏覽器之渲染引擎(WebKit)

前言

一探瀏覽器幕後的《三倆事》上一篇的介紹讓你們對瀏覽器的組成有了個模糊的認識:css

  1. 瀏覽器是什麼?
  2. 瀏覽器(chromium)基本架構
  3. 瀏覽器主要組件
  4. 瀏覽器內核是什麼?
  5. JavaScript(js)引擎
  6. 渲染引擎

今天呢作渲染模塊(WebKit)展開學習討論。html

首先做爲順序介紹來講。我應該具體介紹實現一個瀏覽器應該包含哪些內容(介紹一下chromium實現包含了哪些內容)。可是考慮在前端話題的介紹。只作簡單的列舉不作具體展開討論;有興趣能夠自行下去學習。前端

其次按你們耳熟能詳的程度來講的話我應該着重討論一下JS引擎(V8 event-loop相關);可是本節內容仍是想先介紹討論一下渲染引擎(WebKit)。不爲別的我就是喜歡玩~ 由於我寫這個系列的受衆仍是偏向前端,因此涉及到須要代碼介入講解的部分這邊採起JavaScript來實現。node

渲染引擎

1621765871(1).jpg

畫架構圖畫習慣了,不要在乎醜陋的細節 - -.
由上圖能夠獲得渲染引擎內部解析執行大概過程的信息:
  1. 當訪問頁面的時候會進行網絡請求(network)去獲取頁面的內容;當若是命中緩存(cache)就會從存儲(memory)上直接讀取。
  2. HTMLParser 進行html解析經過特定標識進行分塊。可解析爲CSS DOM JS幾個模塊。
  3. CSSParser 進行css也是解析。
  4. DOMParser 進行DOM結構解析。(構建過程當中發現是JS部分執行5,若是是資源進行異步請求.不會阻礙解析)。
  5. JavaScript 部分丟給JS引擎進行解析。(若是有操做DOM/CSS部分會影響以前的解析,同時會阻礙解析)。
  6. 經過上文解析內容構建RenderTree。
  7. 解析完成經過renderTree進行佈局和繪製。
  8. 將最終圖像顯示在屏幕上。

下面對於上文所提部分進行展開討論一下 flow me~ gogogo!web


Parser 解析

上面Parser的字眼是否是太多了。咱們獲得一個信息解析是渲染引擎中很是重要的一個環節,因此首先須要介紹一下解析.正則表達式

解析文檔是指將文檔轉化成爲有意義的結構,也就是可以讓代碼理解和使用的結構。解析獲得的結果一般是表明了文檔結構的節點樹,它稱做解析樹或者語法樹。編程

解析是以文檔所遵循的語法規則(編寫文檔所用的語言或格式)爲基礎的。比方說HTML解析那麼是在HTML4/5的規範上進行的。全部能夠解析的格式都必須對應肯定的語法(由詞彙和語法規則構成)。canvas

image011.png

解析過程一般是分紅兩個子過程:詞法分析和語法分析
詞法分析是將輸入內容分割成大量標記的過程。(進行切分可識別大量標記的詞段)語法分析是應用語言的語法規則的過程(到底這段語言是要作什麼工做)。segmentfault

HTMLParser

HTML 解析器的任務是將 HTML 標記解析成解析樹。瀏覽器

HTML 語法定義:
HTML 的詞彙和語法在 W3C 組織建立的規範中進行了定義。

DOM
解析器的輸出「解析樹」是由 DOM 元素和屬性節點構成的樹結構。DOM 是文檔對象模型 (Document Object Model) 的縮寫。它是 HTML 文檔的對象表示,同時也是外部內容(例如 JavaScript)與 HTML 元素之間的接口。
解析樹的根節點是Document對象。

image.png

DOM解析code示意(不能運行的) 首先要了解解析過程必定是迭代過程:

// 分析標記<>和屬性的正則表達式
    var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[\w-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
        endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
        attr = /([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
    function ParserHTML(html){
         // 接收參數html
        while(html){

            // 匹配註釋內容
            if(html.indexOf("<!--") == 0){
                //
            }
            // 匹配開始標籤
            if (html.indexOf("<") == 0) {
                match = html.match(startTag);

                if (match) {
                  html = html.substring(match[0].length); //匹配截取
                  //繼續迭代
                }
           }
           if(html.indexOf("</") == 0){
               //匹配結束標籤操做
           }

        }
    }
    // 輸出示意結構
    {
        element:"",
        parentNode:{},
        childrenNode:{},
        content:"",
        ....
    }
    // 我以前寫的找不到了--, 你們能夠試着去實現一下。有問題隨時溝通 本示例正則可用。

CSSParser

CSS解析同HTML不一樣地方是上下文無關的語法;可經過不少解析器作解析。
詞法語法(詞彙)是針對各個標記用正則表達式定義的:

  • comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/
  • num [0-9]+|[0-9]*"."[0-9]+
  • nonascii [\200-\377]
  • nmstart [_a-z]|{nonascii}|{escape}
  • nmchar [_a-z0-9-]|{nonascii}|{escape}
  • name {nmchar}+
  • ident {nmstart}{nmchar}*
  • 經過 CSS 語法文件自動建立解析器, 會建立自下而上的移位歸約解析器。會將 CSS 文件解析成 StyleSheet 對象,且每一個對象都包含 CSS 規則。CSS 規則對象則包含選擇器和聲明對象,以及其餘與 CSS 語法對應的對象。以下:

image023.png

RenderTree

在 DOM 樹構建的同時,瀏覽器還會構建另外一個樹結構:渲染樹。這是由可視化元素按照其顯示順序而組成的樹,也是文檔的可視化表示。它的做用是讓您按照正確的順序繪製內容。

WebKits RenderObject 類是全部渲染樹的基類,其定義以下:

class RenderObject{
  virtual void layout();  //佈局
  virtual void paint(PaintInfo);  //繪製
  virtual void rect repaintRect(); //從新繪製Rect
  Node* node;  // DOM節點
  RenderStyle* style;  // 計算render style
  RenderLayer* containgLayer; //render layer 
}
渲染樹和 DOM 樹的關係

渲染樹是和 DOM 元素相對應的,但並不是一一對應。非可視化的 DOM 元素不會插入渲染樹中,例如「head」元素。若是元素的 display 屬性值爲「none」,那麼也不會顯示在渲染樹中(可是 visibility 屬性值爲「hidden」的元素仍會顯示)。

關於多渲染樹的例子是格式無效的 HTML。根據 CSS 規範,inline 元素只能包含 block 元素或 inline 元素中的一種。若是出現了混合內容,則應建立匿名的 block 渲染樹,以包裹 inline 元素。

有一些渲染對象對應於 DOM 節點,但在樹中所在的位置與 DOM 節點不一樣。浮動定位和絕對定位的元素就是這樣,它們處於正常的流程以外,放置在樹中的其餘地方,並映射到真正的框架,而放在原位的是佔位框架。

佈局

建立完成渲染樹時,並不包含位置和大小信息。計算這些值的過程稱爲佈局或重排。

HTML 採用基於流的佈局模型,這意味着大多數狀況下只要一次遍歷就能計算出幾何信息。處於流中靠後位置元素一般不會影響靠前位置元素的幾何特徵,所以佈局能夠按從左至右、從上至下的順序遍歷文檔。可是也有例外狀況,好比 HTML 表格的計算就須要不止一次的遍歷 (3.5)。

座標系是相對於根框架而創建的,使用的是上座標和左座標。

佈局是一個遞歸的過程。它從根渲染樹(對應於 HTML 文檔的 <html> 元素)開始,而後遞歸遍歷部分或全部的框架層次結構,爲每個須要計算的渲染樹計算幾何信息。

根渲染樹的位置左上角 是 0,0,(跟canvas2D座標規則一致)其尺寸爲視口(也就是瀏覽器窗口的可見區域)。
全部的渲染樹都有一個layout或者reflow方法,每個渲染樹將會調用其須要進行佈局的子代的 layout 方法。

繪製

在繪製階段,系統會遍歷渲染樹,並調用渲染樹的paint方法,將渲染樹的內容顯示在屏幕上。繪製工做是使用用戶界面基礎組件完成的。

繪製的順序其實就是元素進入堆棧樣式上下文的順序;從後向前。塊渲染樹的堆棧順序以下:

  1. 背景顏色
  2. 背景圖片
  3. 邊框
  4. 子代
  5. 輪廓

繪製實現可參考canvas(canvas必定要學的啊 時刻關注一些前沿的動做),瞭解基礎圖元繪製方法與過程。例如繪製line(須要座標 基礎圖元結構等等):

image.png

圖片來源

最後

距離上一篇文章的生活日記: 20210521那天呢照樣開心(朋友圈依舊不活躍)。20210522呢不開心。20210523也就是今天總的來講過得去 並無學習。(勞逸結合 哈哈)

而後呢由於最近有人總微信我編程範式到底選擇哪一種好一點呢? 我如今用的更可能是什麼編程範式呢? 本身寫代碼維護起來好睏難怎麼辦呢?... (作了好久的解答,應該記錄下來提供給你們)下期JS引擎以前 我會把編程範式作一個簡單介紹供你們參考 小小插曲(預計明天)。

加油!有問題請你們隨時留言,看到必定會回覆的。對於上篇補架構圖...好吧是我鴿了 我看也沒人催..懶得畫了

相關參考

  1. CSS 的詞法和語法
  2. 處理模型
  3. WebCore Rendering
  4. WebKit官網
  5. WebKit介紹
  6. canvas相關
相關文章
相關標籤/搜索