日本XXXX裸体XXXX在线观看,乱中年女人伦AV一区二区,色狠狠一区二区三区熟女

  • <em id="d9ukz"><acronym id="d9ukz"><input id="d9ukz"></input></acronym></em>
  • 【春招必備】一名合格的初中級前端工程師需要掌握的瀏覽器渲染筆記

    來源:掘金
    2021-03-12
    2366
    導語:本文整體思路主要參考極客時間專欄-瀏覽器工作原理與實踐(推薦,講的不錯),文中部分圖片畫起來比較復雜,也直接采用了文中的圖片,僅供學習。
    閱讀本文大概需要8分鐘

    前言

    瀏覽器渲染這是一個廣而深的題目,其中的每一個點如果深入,都可以講一整天。本文主要從廣度的層面,梳理了瀏覽器的整體渲染流程,有不對的地方,煩請指正!

    ps:本文整體思路主要參考極客時間專欄-瀏覽器工作原理與實踐(推薦,講的不錯),文中部分圖片畫起來比較復雜,也直接采用了文中的圖片,僅供學習。

    整體流程

    • 1、解析HTML,構建DOM樹
    • 2、解析CSS,生成CSS規則樹
    • 3、合并DOM樹和CSS規則,生成Render樹(頁面布局)
    • 4、繪制Render樹(paint),繪制頁面像素信息
    • 5、顯示

    1、構建DOM樹

    因為瀏覽器無法直接理解和使用html,所以需要將html轉換為瀏覽器能夠理解的結構——DOM樹。 在渲染引擎內部,有一個叫 HTML 解析器(HTMLParser)的模塊,它的職責就是負責將 HTML 字節流轉換為 DOM 結構。

    • 1、解碼:瀏覽器將接收的字節流(Bytes)基于編碼方式解析為字符(characters)
    • 2、分詞:通過分詞器(也就是詞法分析)將字符轉換為 Token,分為Tag Token 和文本Token
    • 3、tokens->nodes
    • 4、nodes->DOM

    第3步和第4步其實是同時進行的,需要將 Token 解析為 DOM 節點,并將 DOM 節點添加到 DOM 樹中。此過程HTML 解析器通過維護了一個Token棧結構來完成。

    • 如果是StartTag Token,就會創建一個DOM節點,并推入棧
    • 如果是文本 Token,就會生成一個文本節點,然后將直接該節點加入到 DOM 樹中
    • 如果是EndTag Token,會查看棧頂元素是否為對應的StartTag Token,如果是則彈出,該節點解析完成。

    具體實現可以參考Vue.js的HTMLParser實現

    2、構建CSS規則樹

    與HTML文本一樣,渲染引擎也沒法直接理解CSS文本,因此渲染引擎會將其轉換為其能理解的結構——styleSheets。在控制臺執行document.styleSheets 可以看到:

    styleSheets是對頁面樣式的一個總覽,其內部層級如下圖:

    關于stylesheets的具體屬性,可參考鏈接

    針對styleSheets,結合CSS的繼承、優先級層疊等規則,渲染引擎最終生成如下CSS規則樹:

    此時每個元素上的樣式就是最終應用這個元素上的樣式了,通過瀏覽器的Element->Computed可以查看。

    3、布局Layout

    頁面結構和頁面樣式都確定了,接下來就需要將兩者結合起來,對頁面進行整體布局。

    3.1構建Render樹

    DOM樹只是描述了源碼中HTML的結構,但其中許多元素并不需要展示在畫面中(比如head、dispaly:none),也有一些不存在DOM樹中但需要顯示在頁面上的元素(比如偽類),因此在顯示之前需要遍歷DOM樹中的所有節點,忽略掉不可見元素,添加不存在DOM樹中但需要顯示的的內容,最終生成一棵只包含可見元素的Render樹。

    3.2計算布局信息

    以上得到了每個DOM元素的文檔結構和樣式,但是還不知道元素的具體絕對幾何位置。 比如一個div元素的樣式如下:

    div {
        position: absolute;
        width:100px;
        height:100px;
        top:10px;
        left:10px;
    }
    復制代碼

    那么我們還需要知道它的具體絕對幾何位置:

    div {
        x: ?
        y: ?
        width: 100px
        height: 100px
    }
    復制代碼

    而計算元素的具體絕對幾何位置是一項艱巨的任務,因為即使是最簡單的頁面布局(如從上到下的塊流程)也必須考慮字體的大小以及在何處換行,因為這會影響段落的大小和形狀,也會影響下一段的位置。

    在Chrome中,有一整個工程師團隊在為布局而工作, few talks from BlinkOn Conference 有提到一些,大家感興趣可以看看。

    4、繪制paint

    以上得到了完整的Render樹,也就是知道了頁面的樣式和位置信息,但還沒到繪制的時候。類似于畫一幅畫,我們還需要知道頁面各元素的繪制順序,比如需要先畫藍天再畫白云,否則白云會被藍天覆蓋住。 針對繪制順序,因為頁面中有很多復雜的效果,如一些復雜的 3D 變換、頁面滾動,或者使用 z-index做 z 軸排序等,為了更加方便地實現這些效果,渲染引擎采用了分層機制。

    4.1分層layer

    每個DOM元素會有自己的布局信息Layout Object, 根據其布局信息的層級等關系,某些Layout Object會擁有共同的渲染層Paint Layer,某些Paint Layer又會擁有共同的合成層Composite Layer(Graphic Layers)。

    分層-渲染層(Paint Layer)

    如上圖,DOM 樹中得每個 Node 節點都有一個對應的 LayoutObject;擁有相同的坐標空間的 LayoutObjects,屬于同一個渲染層(PaintLayer)。渲染層產生的最普遍條件是“層疊上下文”。

    層疊上下文示意圖:

    根據層疊上下文-MDN,層疊上下文由滿足以下任意一個條件的元素形成:

    滿足以上任一條件的元素,都會擁有自己的渲染層,其子元素若沒有單獨的渲染層,則隨父級元素同一層。

    其他產生渲染層的特殊場景(除“層疊上下文”),可參考鏈接

    分層-合成層(Composite Layer)

    某些特殊的渲染層會被認為是合成層(Composite Layer),合成層擁有單獨的 GraphicsLayer。 渲染層與合成層的區別,如圖:

    產生合成層的具體條件可參考文章,這里列出幾個常見的場景:

    • 有 3D transform
    • 對 opacity、fliter、transform 應用了 animation 或者 transition(需要是 active 的 animation 或者 transition,當 animation 或者 transition 效果未開始或結束后,提升合成層也會失效)
    • will-change 設置為 opacity、transform、top、left、bottom、right(其中 top、left 等需要設置明確的定位屬性,如 relative 等

    以上三種原因生成合成層demo代碼如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <style type="text/css">
            *{
                    margin:0;
                    padding:0;
            }
            div{
                    width:200px;
                    height:100px;
            }
            .default{
                    background: #ffb284;
            }
            .composite-translateZ{
                    transform: translateZ(0);
                    background: #f5cec7;
            }
            .composite-tansform-active{
                    background: #e79796;
                    transform: translate(0,0);
                    transition: 3s;
            }
            .composite-tansform-active:hover{
                    transform: translate(100px,100px);        
            }
            .composite-will-change{
                    background: #ffc988;
                    will-change: transform;
            }
        </style>
    </head>
    <body>
        <div class='default'>默認層</div>
        <div class='composite-translateZ'>合成層-translateZ</div>
        <div class='composite-tansform-active'>合成層——active transform(hover一下我)</div>
        <div class='composite-will-change'>合成層——will-change</div>
    </body>
    </html>
    復制代碼

    在控制臺的Layers下,可以看到合成層。

    • overlap:元素覆蓋在其他合成層元素上,則該元素會被隱式提升為合成層,demo代碼如下
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <style type="text/css">
            *{
                    margin:0;
                    padding:0;
            }
            div{
                    width:200px;
                    height:200px;
                    /*background: */
            }
            .bottom{
                    background: #f5cec7;
                    animation: anim-translate 3s ease-in-out alternate infinite both;
            }
            @keyframes anim-translate {
                from { transform: translateX(0); }
                to { transform: translateX(50px); }
            }
            .top{
                    background: #e79796;
                    transform: translateY(-50px);
            }       
        </style>
    </head>
    <body>
        <div class='parents'>
            <div class="bottom">下層-有動畫</div>
            <div class="top">上層-隱式提升為合成層</div>
        </div>
    </body>
    </html>
    復制代碼

    demo中的上層div,本不具備提升為合成層的因素,但由于其覆蓋在了下層div上,如果上層div不隱式提升為合成層,它就會和和父元素共用一個合成層,此時渲染順序就會出錯。為了保證渲染順序,因此上層被隱式提升為合成層。在控制臺也可以看到原因:might overlap other composited content.

    渲染層是為保證頁面元素以正確的順序,合成層是為了減少渲染的開銷。

    提升為合成層的好處:

    • 合成層的位圖,會采用硬件加速,也就是會交由 GPU 合成,比 CPU 處理要快
    • 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
    • 對于已提升為合成層中的 transform 和 opacity 效果,都只是幾何變換,透明度變換等,不會觸發 layout 和 paint,直接由GPU完成即可

    因此,在開發中,建議對于需要頻繁移動的元素,建議將其提升為單獨的合成層,可減少不必要的重繪,同時可以利用硬件加速,提高渲染效率。

    4.2層繪制paint

    分好層后,就需要對每個層進行繪制了。繪制并不是一蹴而就,而是像畫畫一樣,是按順序一筆一筆畫出來的,渲染引擎也是類似。對于每一個合成層,渲染引擎的渲染過程:

    • 先繪制下面的渲染層,再繪制上面的渲染層;
    • 在繪制一個渲染層時,將一個渲染層的繪制拆分成很多小的繪制指令。

    常見的指令如下:

    • drawReact(rect, paint):使用paint畫一個矩形rect
    • drawTextBlob(x,y,paint):使用paint以x、y為起始坐標繪制文字
    • drawPaint(paint):用paint填充畫布
    • color: 采用ARGB的方式

    各指令的含義可參考鏈接

    打開“開發者工具”的“Layers”標簽,任意選擇一層合成層,可查看該層detail下的詳細渲染列表paint profiler。 繪制指令demo代碼如下

    <!DOCTYPE html>
    <html lang="en">
    <meta http-equiv="Content-Type" Content="text/html; Charset=UTF-8">
    <head>
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            div{
                width:200px;
                height:100px;
                text-align: center;
                line-height:100px;
            }
            p{
                height:40px;
                line-height:40px;
                font-size:20px;
                margin-bottom: 30px;
            }
            .level-default{
                position: absolute;
                background: #f5cec7;
                top: 60px;
            }
            .level1{
                background: #ffb284;
                position: absolute;
                z-index:2;
                top: 160px;
            }
            .level2{
                background: #e79796;
                position: absolute;
                z-index:1;
                top: 260px;
            }
            .composite-1, .composite-2{
                position: relative;
                transform: translateZ(0);
                width:300px;
                height:400px;
                background: #ddd;
                margin-bottom:20px;
            }
        </style>
    </head>
    <body>
        <div class="composite-1">
            <p>合成層一</p>
            <div class='level-default'>默認層</div>
            <div class='level1'>渲染層1:z-index:2</div>
            <div class='level2'>渲染層2:z-index:1</div>
        </div>
        <div class="composite-2">
            <p>合成層二</p>
            <div class='level-default'>默認層</div>
            <div class='level1'>渲染層1:z-index:2</div>
            <div class='level2'>渲染層2:z-index:1</div>
        </div>
    </body>
    </html>
    復制代碼

    比如選擇composite-1的合成層,繪制列表如下:

    這里順便也可以看到一點:渲染層2在渲染層1的后面,但由于其z-index較大(說明其渲染層層級較高),因此優先渲染層2。

    需要說明一點,繪制列表只是用來記錄繪制順序和繪制指令的列表,并沒有真正的繪制出頁面。

    4.3柵格化

    生成了繪制指令,就到了真正繪制頁面的時候了,真正的繪制過程不是在主線程完成的,而是在得到繪制指令后,主線程會將這些信息交給合成線程,由合成線程來完成繪制。

    合成線程是如何工作的呢?

    頁面可能很大,但用戶只能看到一部分,在這種情況下如果全部繪制,就會產生很大的性能開銷,因此需要優先繪制視口(即用戶看到的區域)區域內的元素。

    基于此原因,繪制前,合成線程會對頁面進行分塊,然后將每個圖塊發送給柵格線程,柵格線程將圖塊轉換為位圖。合成器線程可以優先處理不同的柵格線程,這樣就可以首先對視口(或附近)中的事物進行柵格化。

    通常,柵格化過程都會使用 GPU 來加速生成,生成的位圖被保存在 GPU 內存中。

    柵格化的過程:

    5、合成與顯示

    柵格化完成后,每一個圖層都對應一張“圖片”,合成線程會將這些圖片合成為一張“圖片”。此時,頁面數據已經完成繪制,現在只需要顯示給用戶即可,此時就需要顯卡和顯示器就上場了。

    顯卡分為前緩沖區和后緩沖區,合成線程生成的“圖片”會被發送至后緩沖區。顯卡對圖片進行處理完成后,系統就會讓后緩沖區和前緩沖區互換,這樣顯示器就總能讀到顯卡最新產生的數據了。

    通常顯卡和顯示器的刷新頻率是一致的,都會60次/秒,但對于一些復雜的場景,顯卡處理速度比較慢,顯卡的刷新頻率就會低于顯示器,此時頁面就會出現卡頓現象。

    因此在開發中,我們需要盡量保證一幀畫面的處理總時長(以上的所有步驟)不超過1/60s = 16.7ms,這樣畫面才不會出現卡頓現象。不過量化地衡量渲染時間比較困難,但基于以上分析的渲染過程,我們就可以從渲染的各個步驟著手優化渲染流程,提高渲染效率。

    6、相關拓展

    6.1 CSS如何影響DOM的構建

    JavaScript腳本由于可能會修改DOM,因此會阻塞DOM的構建,這一點我們都知道;而CSS并不會操作或者改變DOM,因此通常我們認為CSS不會影響DOM的構建,只會影響后續的布局、繪制等過程,即會影響DOM的渲染。但其實CSS可以通過JavaScript來阻塞DOM的構建。

    因為JavaScript是可以改變樣式的,也就是具有修改CSS規則樹的能力,而JavaScript腳本里是否有改變樣式的操作,這一點在執行JavaScript之前是不可知的。因此,為保證JavaScript腳本的正確執行,在執行JavaScript之前,CSS規則樹必須要先準備好(不然萬一有修改CSS的操作呢)。

    也就是說,若在構建DOM的中途存在阻塞DOM構建的JavaScript腳本,而此頁面中還包含了外部 CSS 文件的引用,那么此時就需要等目前的CSS規則樹(基于目前生成完的部分DOM樹)構建完畢后,再開始JavaScript腳本的執行,等一切結束了,再繼續DOM的構建。

    整個流程如圖:(其中CSSOM表示CSS規則樹)

    demo代碼如下:

    <!DOCTYPE html>
    <html lang="en">
    <meta http-equiv="Content-Type" Content="text/html; Charset=UTF-8">
    <head>     
        <style type="text/css">
            h4{
                font-size:18px;
                font-weight:none;
            }
        </style>
        <link rel="stylesheet" type="text/css" href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/css/soutu_new2_ae491b7.css">
    </head>
    <body>
        <button id="btn">清空dom</button>
        <div>我是div</div>
        <!-- !!!script阻塞div的構建 -->
        <script>
                console.log('遇到內聯script啦')
        </script>
    
        <div>我是div</div>
        <div>我是div</div>
        <div>我是div</div>
        <div>我是div</div>
    </body>
    <script type="text/javascript">
        let btn = document.getElementById('btn')
        let body = document.body
        btn.addEventListener("click", function(e){
                body.innerHTML = ''
        }, true);
    </script>
    </html>
    復制代碼

    將控制臺Network中的網絡調為Slow 3G,點擊按鈕清空dom后,刷新頁面觀察Element中DOM元素出現的時機。

    • 當不存在script時,所有div全部很快出現
    • 存在script時,script后的div元素要等一會(css加載完成)才會出現

    說明CSS可以通過JavaScript來阻塞DOM的構建。

    6.2重排、重繪、合成

    • 重排會改變元素的幾何位置,需要更新完整的渲染流水線,所以開銷也是最大的
    • 重繪只是修改元素的顏色等非位置屬性,所以省去了布局和分層階段,開銷比重排小
    • 合成只會由已提升會合成層的transform或opacity觸發,只涉及幾何變換或透明度變換,會跳過前面的流程,直接進入合成階段,開銷最小。(transform或opacity若未提升為合成層,則依然會觸發paint

    另外在合成小節提到,生成繪制指令之后的分開、柵格化等工作是在合成線程中進行,這也就意味著在執行合成操作時,是不會影響到主線程執行的,這也是合成動畫性能好的原因之一。也就揭示了為什么經常主線程卡住了,但是 CSS 動畫依然能執行的原因。

    6.3層爆炸

    前面提到overlap會導致生成隱式合成層,極端情況下就可能會產生大量的不在預期內的額外合成層,導致層爆炸。demo

    因此,在開發過程中,建議:

    • 為防止層爆炸,在提升為合成層的元素上,建議加上z-index,防止overlap引起的層提升。
    • 不要創建太多層,因為每層都需要內存和管理開銷;不要在不分析的情況下提升元素。

    7、參考



    相關標簽
    免費獲取專屬 《策劃方案 》及報價
    免費體驗我們的業務系統、OA系統、在線教育、電商系統、智慧辦公等產品定制化方案,助力您的信息化發展之路
    即時交流
    在線咨詢 電話咨詢
    在線咨詢
    產品經理

    一對一產品經理

    180 8812 7777
    電話咨詢

    電話咨詢

    0871-6718 6978
    到訪面聊
    返回頂部
    日本XXXX裸体XXXX在线观看,乱中年女人伦AV一区二区,色狠狠一区二区三区熟女