瀏覽器相關原理(面試題)詳細總結二

来源:https://www.cnblogs.com/LuckyWinty/archive/2019/10/18/11699901.html
-Advertisement-
Play Games

1. 瀏覽器渲染過程是怎樣的? 按照渲染的時間順序,流水線可分為如下幾個子階段: 、`樣式計算 佈局階段 分層 柵格化 顯示`。 1. 渲染進程將 HTML 內容轉換為能夠讀懂DOM 樹結構。 2. 渲染引擎將 CSS 樣式表轉化為瀏覽器可以理解的styleSheets,計算出 DOM 節點的樣式。 ...


1. 瀏覽器渲染過程是怎樣的?

按照渲染的時間順序,流水線可分為如下幾個子階段:構建 DOM 樹樣式計算佈局階段分層柵格化顯示

  1. 渲染進程將 HTML 內容轉換為能夠讀懂DOM 樹結構。
  2. 渲染引擎將 CSS 樣式表轉化為瀏覽器可以理解的styleSheets,計算出 DOM 節點的樣式。
  3. 創建佈局樹,並計算元素的佈局信息。
  4. 對佈局樹進行分層,並生成分層樹。
  5. 為每個圖層生成繪製列表,並將其提交到合成線程。合成線程將圖層分圖塊,並柵格化將圖塊轉換成點陣圖。
  6. 合成線程發送繪製圖塊命令給瀏覽器進程。瀏覽器進程根據指令生成頁面,並顯示到顯示器上。

瀏覽器從網路或硬碟中獲得HTML位元組數據後會經過一個流程將位元組解析為DOM樹,先將HTML的原始位元組數據轉換為文件指定編碼的字元,然後瀏覽器會根據HTML規範來將字元串轉換成各種令牌標簽,如html、body等。最終解析成一個樹狀的對象模型,就是dom樹;

獲取css,獲取style標簽內的css、或者內嵌的css,或者當HTML代碼遇見標簽時,瀏覽器會發送請求獲得該標簽中標記的CSS,當渲染引擎接收到 CSS 文本時,會執行一個轉換操作,將 CSS 文本轉換為瀏覽器可以理解的styleSheets

創建佈局樹,遍歷 DOM 樹中的所有可見節點,並把這些節點加到佈局中;而不可見的節點會被佈局樹忽略掉,如 head 標簽下麵的全部內容,再比如 body.p.span 這個元素,因為它的屬性包含 dispaly:none,所以這個元素也沒有被包進佈局樹。最後計算 DOM 元素的佈局信息,使其都保存在佈局樹中。佈局完成過程中,如果有js操作或者其他操作,對元素的顏色,背景等作出改變就會引起重繪,如果有對元素的大小、定位等有改變則會引起迴流。

因為頁面中有很多複雜的效果,如一些複雜的 3D 變換、頁面滾動,或者使用 z-indexing 做 z 軸排序等,為了更加方便地實現這些效果,渲染引擎還需要為特定的節點生成專用的圖層,並生成一棵對應的圖層樹。

渲染引擎實現圖層的繪製,把一個圖層的繪製拆分成很多小的繪製指令然後再把這些指令按照順序組成一個待繪製列表,當圖層的繪製列表準備好之後,主線程會把該繪製列表提交給合成線程,合成線程會將圖層劃分為圖塊,然後按照視口附近的圖塊來優先生成點陣圖(實際生成點陣圖的操作是由柵格化來執行的。所謂柵格化,是指將圖塊轉換為點陣圖)

一旦所有圖塊都被光柵化,合成線程就會生成一個繪製圖塊的命令,然後將該命令提交給瀏覽器進程,瀏覽器最後進行顯示。

2.如何理解迴流和重繪?

迴流:當我們對 DOM 的修改引發了 DOM 幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時,瀏覽器需要重新計算元素的幾何屬性(其他元素的幾何屬性和位置也會因此受到影響),然後再將計算的結果繪製出來。這個過程就是迴流(也叫重排)。

重繪:當我們對 DOM 的修改導致了樣式的變化、卻並未影響其幾何屬性(比如修改了顏色或背景色)時,瀏覽器不需重新計算元素的幾何屬性、直接為該元素繪製新的樣式(跳過了上圖所示的迴流環節)。這個過程叫做重繪。 由此我們可以看出,重繪不一定導致迴流,迴流一定會導致重繪。

常見的會導致迴流的元素:
  • 常見的幾何屬性有 width、height、padding、margin、left、top、border 等等。
  • 最容易被忽略的操作:獲取一些需要通過即時計算得到的屬性,當你要用到像這樣的屬性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 時,瀏覽器為了獲取這些值,也會進行迴流。
  • 當我們調用了 getComputedStyle 方法,或者 IE 里的 currentStyle 時,也會觸發迴流。原理是一樣的,都為求一個“即時性”和“準確性”。
避免方式:
  1. 避免逐條改變樣式,使用類名去合併樣式
  2. 將 DOM “離線”,使用DocumentFragment
  3. 提升為合成層,如使用will-change
#divId {
  will-change: transform;
}

優點

  • 合成層的點陣圖,會交由 GPU 合成,比 CPU 處理要快
  • 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
  • 對於 transform 和 opacity 效果,不會觸發 layout 和 paint

註意:

部分瀏覽器緩存了一個 flush 隊列,把我們觸發的迴流與重繪任務都塞進去,待到隊列里的任務多起來、或者達到了一定的時間間隔,或者“不得已”的時候,再將這些任務一口氣出隊。但是當我們訪問一些即使屬性時,瀏覽器會為了獲得此時此刻的、最準確的屬性值,而提前將 flush 隊列的任務出隊。

3.渲染引擎什麼情況下才會為特定的節點創建新的圖層?

層疊上下文是HTML元素的三維概念,這些HTML元素在一條假想的相對於面向(電腦屏幕的)視窗或者網頁的用戶的z軸上延伸,HTML元素依據其自身屬性按照優先順序順序占用層疊上下文的空間。

  1. 擁有層疊上下文屬性的元素會被提升為單獨的一層。

擁有層疊上下文屬性:

  • 根元素 (HTML),
  • z-index 值不為 "auto"的 絕對/相對定位元素,
  • position,固定(fixed) / 沾滯(sticky)定位(沾滯定位適配所有移動設備上的瀏覽器,但老的桌面瀏覽器不支持)
  • z-index值不為 "auto"的 flex 子項 (flex item),即:父元素 display: flex|inline-flex,
  • z-index值不為"auto"的grid子項,即:父元素display:grid
  • opacity 屬性值小於 1 的元素(參考 the specification for opacity),
  • transform 屬性值不為 "none"的元素,
  • mix-blend-mode 屬性值不為 "normal"的元素,
  • filter值不為"none"的元素,
  • perspective值不為"none"的元素,
  • clip-path值不為"none"的元素
  • mask / mask-image / mask-border不為"none"的元素
  • isolation 屬性被設置為 "isolate"的元素
  • 在 will-change 中指定了任意CSS屬性(參考 這篇文章
  • -webkit-overflow-scrolling 屬性被設置 "touch"的元素
  • contain屬性值為"layout","paint",或者綜合值比如"strict","content"
  1. 需要剪裁(clip)的地方也會被創建為圖層。

這裡的剪裁指的是,假如我們把 div 的大小限定為 200 * 200 像素,而 div 裡面的文字內容比較多,文字所顯示的區域肯定會超出 200 * 200 的面積,這時候就產生了剪裁,渲染引擎會把裁剪文字內容的一部分用於顯示在 div 區域。出現這種裁剪情況的時候,渲染引擎會為文字部分單獨創建一個層,如果出現滾動條,滾動條也會被提升為單獨的層。

4.JavaScript 是如何支持塊級作用域的?

塊級作用域就是通過詞法環境的棧結構來實現的,而變數提升是通過變數環境來實現,通過這兩者的結合,JavaScript 引擎也就同時支持了變數提升和塊級作用域了。

詞法環境跟函數上下文,都是通過棧結構實現的。函數內部通過 var 聲明的變數,在編譯階段全都被存放到變數環境(函數上下文)中,而通過let和const申明的變數會被追加到詞法環境中,當這個塊執行結束之後,追加到詞法作用域的內容又會銷毀掉。

舉個例子:

function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    {
        console.log(myname) 
        let myname= 'winty'
    }
    console.log(test,'---',myname) 
}
foo()
//思考一下會輸出什麼?

執行到第一個console.log前的執行上下文是這樣的:

GitHub

從圖中看,第一個console.log理論上應該輸出 undefined。但是語法規定了一個"暫時性死區(TDZ,當進入它的作用域,它不能被訪問(獲取或設置)直到執行到達聲明)",也就是說雖然通過let聲明的變數已經在詞法環境中了,但是在沒有賦值之前,訪問該變數JavaScript引擎就會拋出一個錯誤。

因此,第一個console.log會拋錯,[Uncaught ReferenceError: Cannot access 'myname' before initialization]。拋錯則函數會中斷執行,為了能讓我們的代碼繼續分析,我們先加個 try-catch ,然後繼續分析:

function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    try{
        {
            console.log(myname) 
            let myname= 'winty'
        }
    }catch(ex){
        console.error(ex)
    }
    console.log(test,'---',myname) 
}
foo()
//思考一下會輸出什麼?

執行到第二個console.log前的執行上下文是這樣的:

GitHub

此時,{}塊作用域中的內容已執行完畢,被銷毀掉了。第二個console.log會輸出1 "---" "LuckyWinty"

5. JavaScript 中的數據是如何存儲在記憶體中的?

在 JavaScript 中,原始類型的賦值會完整複製變數值,而引用類型的賦值是複製引用地址。

在 JavaScript 的執行過程中, 主要有三種類型記憶體空間,分別是代碼空間棧空間堆空間
其中的代碼空間主要是存儲可執行代碼的,原始類型(Number、String、Null、Undefined、Boolean、Symbol、BigInt)的數據值都是直接保存在“棧”中的,引用類型(Object)的值是存放在“堆”中的。因此在棧空間中(執行上下文),原始類型存儲的是變數的值,而引用類型存儲的是其在"堆空間"中的地址,當 JavaScript 需要訪問該數據的時候,是通過棧中的引用地址來訪問的,相當於多了一道轉手流程。

在編譯過程中,如果 JavaScript 引擎判斷到一個閉包,也會在堆空間創建換一個“closure(fn)”的對象(這是一個內部對象,JavaScript 是無法訪問的),用來保存閉包中的變數。所以閉包中的變數是存儲在“堆空間”中的。

JavaScript 引擎需要用棧來維護程式執行期間上下文的狀態,如果棧空間大了話,所有的數據都存放在棧空間裡面,那麼會影響到上下文切換的效率,進而又影響到整個程式的執行效率。通常情況下,棧空間都不會設置太大,主要用來存放一些原始類型的小數據。而引用類型的數據占用的空間都比較大,所以這一類數據會被存放到堆中,堆空間很大,能存放很多大的數據,不過缺點是分配記憶體和回收記憶體都會占用一定的時間。因此需要“棧”和“堆”兩種空間。

參考資料

  • 極客時間《瀏覽器工作原理與實踐》

最後

  • 歡迎加我微信(winty230),拉你進技術群,長期交流學習...
  • 歡迎關註「前端Q」,認真學前端,做個有態度的技術人...

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.備份: 2.恢復: 3.實例: ...
  • 近期內手裡收到一些朋友發來的簡歷,讓我初步把把關。在看了多份簡歷之後,想說一些話,給大家講講我對於簡歷的看法。 簡歷要突出重點,要精準無誤,要言之有物,要有的放矢。 如何突出重點,校園招聘可以寫一些校園獲獎情況,你畢業了多年應聘,簡歷上寫個大學長跑一等獎,是要突出自己不怕加班,體質好?這個大學生活內 ...
  • 谷歌訪問助手安裝教程參考:https://www.cnblogs.com/waiwai14/p/11697371.html 谷歌訪問助手下載地址:https://pan.baidu.com/s/1YHjj8La5sC2QLhQ8Aga-sQ 由於谷歌中國的退出,導致本來可以訪問到的官網無法訪問或者無 ...
  • 問題及分析 今天做項目的時候,執行pod update報瞭如下錯誤信息: /usr/local/lib/ruby/gems/2.4.0/gems/cocoapods 1.5.3/lib/cocoapods/command.rb:118:in git version sudo xcode select ...
  • 終於學JavaScript了! 數組的相關參數 數組 刪除第一個元素, 刪除最後一個元素,返回的都是刪除的元素, 在開頭增加一個元素, 在結尾增加元素, 用於刪除和修改, 用於切片. 數據類型轉換 一元的 + 運算符可用於把變數轉換為數字:如果無法轉換變數,則仍會成為數字,但是值為 NaN(Not ...
  • 先上圖 ...
  • 先上圖 ...
  • 1.DOM操作 常用的DOM操作 DOM樹中元素與元素之間的關係:父子、兄弟 假如現在有一個DOM元素為obj 找DOM節點 1.找父節點 2.找兄弟節點 3.找子節點 DOM節點操作 在DOM操作里,所有的創建、插入、刪除操作,都必須通過父節點來插入子節點和刪除子節點 節點文本操作: 控制節點文本 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...