1. 瀏覽器渲染原理

        共 3619字,需瀏覽 8分鐘

         ·

        2023-11-09 02:04

        前言
        當我們輸入一個url之后,頁面會在很短時間內(nèi)呈現(xiàn)給我們。這個過程其實相當復(fù)雜,瀏覽器在背后給我做了很多的事。除去一些諸如DNS解析的等邊角料工作,整體可以大致分為網(wǎng)絡(luò)渲染。

        渲染
        瀏覽器的網(wǎng)絡(luò)線程接收到HTML文檔后,會生成一個
        渲染任務(wù),并將其添加到渲染主線程消息隊列。在事件循環(huán)機制的作用下,渲染主線程會取出消息隊列中的渲染任務(wù),開啟渲染流程。

        整個渲染流程分為多個階段:解析HTML、計算樣式、布局、分層、繪制、分塊、光柵化、。每個階段都有明確的輸入輸出,上一階段的輸出會成為下一階段的輸入,這樣整個渲染過程就形成了一條組織嚴密的生產(chǎn)流水線。

        解析HTML-Parse HTML
        第一步就是解析 HTML,生成 DOM 樹。
        在主線程上解析HTML字符串,得到DOM樹(html元素、文本、注釋等節(jié)點的信息)和CSSOM樹。

        CSSOM樹會包含瀏覽器默認樣式、內(nèi)部樣式、外部樣式、內(nèi)聯(lián)樣式:

        在HTML解析過程,如果遇到了CSS代碼,為了提高效率,瀏覽器會啟動一個預(yù)解析器率先下載外部CSS文件和解析CSS。

        在解析過程中,遇到CSS就解析CSS,遇到JS就執(zhí)行JS。為了提高解析效率,瀏覽器會在解析之前,啟動一個預(yù)解析線程,率先下載外部的CSS文件和JS文件。
        如果主線程解析到link的位置,此時link的CSS資源文件還沒下載解析好,主線程不會等待,繼續(xù)解析后面的HTML。這是因為下載和解析CSS是在預(yù)解析線程中進行的,這就是CSS不會阻塞HTML解析的原因。

        當主線程解析到script的位置時,會停止解析,轉(zhuǎn)而等待下載和執(zhí)行完JS才能繼續(xù)解析。因為JS代碼可能會更改DOM樹,這就是JS阻塞HTML解析的根本原因。
        因此,如果我們想加快首屏的渲染,建議將 script 標簽放在 body 標簽底部。
        當然現(xiàn)代瀏覽器都提供了非阻塞的下載方式,async和defer。
        計算樣式-Recalculate Style
        得到每個節(jié)點計算后的最終樣式,如下圖,我們可以看到任何元素都會有全量的CSS屬性:
        屬性值的計算過程,分為如下4個步驟:
        ?  確定聲明值
        ?  層疊沖突 (重要性、特殊性、源次性
        ?  使用繼承
        ?  使用默認值
        渲染主線程會遍歷整棵DOM樹,依次計算出DOM樹的每個節(jié)點的最終樣式,稱為 Computed Style。在這個過程,很多預(yù)設(shè)值會變成絕對值,相對單位會變成絕對單位。這一步完成之后,將會得到一棵帶有樣式的DOM樹。

        布局-Layout
        根據(jù)每個節(jié)點的樣式信息算出節(jié)點的幾何信息(尺寸和位置),得到布局(Layout)樹。

        對于一個元素來說,它的尺寸和位置經(jīng)常與它的包含塊(containing block)有關(guān),即我們經(jīng)常說的它是相對于哪個元素,例如 width: 100% 。
        如何確定包含塊?
        確定一個元素的包含塊的過程完全依賴于這個元素的
        position屬性:
        staticrelative、sticky:包含塊可能由它的最近的祖先塊元素(如 inline-block、block )
        absolute:由它的最近的 position 的值不是 static 的祖先元素
        fixed:在連續(xù)媒體的情況下包含塊是viewport
        absolutefixed:包含塊也可能是由滿足以下條件的最近父級元素
                ○ 
        transformperspective 的值不是 none。
                ○ 
        will-change的值是 transformperspective。
                ○ filter的值不是 none 
              ○ contain的值是 paint
               backdrop-filter的值不是 none

        DOM樹和Layout樹不一定是一一對應(yīng)的,如隱藏(dispay: none)的元素就不會出現(xiàn)在Layout樹中;
        又如偽元素在DOM樹中并不存在,但是會出現(xiàn)在Layout樹中,因為它擁有幾何信息。

        文本內(nèi)容必須在行盒中,行盒和塊盒不能相鄰。
        如果在塊盒中直接寫入內(nèi)容,則會在中間生成一個匿名行盒;如果塊盒和行盒相鄰,則為行盒外部生成一個
        匿名塊盒。

        重排(Reflow) 的本質(zhì)就是重新計算Layout布局樹。當進行了會影響布局樹的操作后,需要重新計算布局樹,就會引發(fā)重新Layout。 瀏覽器為了避免連續(xù)的多次操作導(dǎo)致布局樹反復(fù)計算,就會合并這些操作,生成一個渲染任務(wù),等到下一次事件循環(huán)再進行計算。所以,改動CSS屬性所造成的Reflow是異步完成的。 正因為如此,當 JS 獲取布局屬性時(如clientWidth),就可能造成無法獲取到最新的布局信息。 于是瀏覽器在反復(fù)權(quán)衡下,最終決定獲取屬性時,立即 Reflow(同步)。

        分層-Layer
        渲染主線程將會使用一套復(fù)雜的策略對整個布局樹進行分層。分層的好處在于,將來某一層改變之后,僅會對該層進行后續(xù)處理,不影響其他分層,從而提升效率。
        比如在google頁面,打開控制臺的Layers,可以查看當前頁面的分層情況。每個瀏覽器都有自己分層策略。滾動條和跟堆疊上下文相關(guān)的屬性都可能影響分層(z-index、opacity、transform),也可以通過will-change屬性更大程度地影響分層結(jié)果。
        繪制(生成繪制指令)-Paint
        首先需要生成繪制的指令,主線程會為每個分層生成繪制指令集,表明如何進行繪制,用于描述這一層的內(nèi)容如何畫出來。

        繪制指令類似于canvas的操作方法:
        移動畫筆到 (x,y) 繪制寬為w,高為h的矩形......

        實際上,canvas是瀏覽器將繪制過程封裝后提供給開發(fā)者的工具。

        重繪(repaint) 的本質(zhì)就是重新根據(jù)分層信息計算了繪制指令。當改動可見樣式后,就需要重新計算繪制指令,引發(fā) Repaint。由于元素的布局(Layout)信息也屬于可見樣式,所以 Reflow 一定會引起 Repaint。


        分塊-Tiling

        分塊會將每一層分成多個小的區(qū)域。
        完成生成繪制指令集之后,主線程會將每個圖層的繪制指令信息提交給合成線程,剩余工作將由合成線程完成。
        合成線程首先會對每個圖層進行分塊,將其劃分成更多的小區(qū)域。它會從線程池中拿出多個線程來完成分塊工作。
        Tips: 合成線程和渲染主線程都位于渲染進程里。
        光柵化-Raster
        分塊完成后,會進入光柵化階段。合成線程會將塊信息交給GPU進程,以極高的速度完成光柵化,GPU會開啟多個線程來完成光柵化,并且優(yōu)先處理靠近視口的塊類似于懶加載策略,以提高性能)。


        光柵化就是將每個塊變成位圖(像素點)。

        畫-Draw
        合成線程會計算出每個位圖在屏幕上的位置,交給
        GPU進行最終的呈現(xiàn)。

        合成線程拿到每個層、每個塊的位圖后,生成一個個的quad(指引)信息,指明位圖信息位于屏幕上的位置,以及會考慮到transform旋轉(zhuǎn)、偏移、縮放等矩陣變換。

        這就是transform效率高的主要原因,因為不會引起樣式的計算、布局、生成繪制指令等,它與渲染主線程無關(guān),這個過程發(fā)生在合成線程中,且只需要進行最后一步-畫。
        如上圖中,為什么合成線程不直接將結(jié)果交給硬件,將內(nèi)容顯示到屏幕上,而要先轉(zhuǎn)交給GPU進程,由GPU進程轉(zhuǎn)發(fā)呢?
        其實是因為合成線程和渲染主線程都屬于渲染進程,渲染進程處于沙盒中,無法進行系統(tǒng)調(diào)度,即無法直接與硬件GPU通信,所以需要GPU進程中轉(zhuǎn)一下。

        總結(jié)
        整體的流程如下:

        瀏覽 2160
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产诱惑视频 | 亚洲最新视频 | 瘦精品无码一区二区三区四区五区六区七区八区 | 女上男下做爰免费观看视频 | 天天日天天射天天操 |