<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          瀏覽器渲染原理

          共 3619字,需瀏覽 8分鐘

           ·

          2023-11-09 02:04

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

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

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

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

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

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

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

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

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

          對于一個元素來說,它的尺寸和位置經(jīng)常與它的包含塊(containing block)有關(guān),即我們經(jīng)常說的它是相對于哪個元素,例如 width: 100% 
          如何確定包含塊?
          確定一個元素的包含塊的過程完全依賴于這個元素的
          position屬性:
          static、relative、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樹中,因?yàn)樗鼡碛袔缀涡畔ⅰ?/span>

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

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

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

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

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

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


          分塊-Tiling

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


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

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

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

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

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

          瀏覽 2160
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产婷婷五月综合亚洲 | 国产亲子乱淫一级a片 | 日无码在线观看 | 台湾午夜成人节目在线播放 | 中文字幕h |