現代瀏覽器內部工做原理(附詳細流程圖)

現代瀏覽器內部工做原理

彙總圖

image
image
image

計算機的核心CPU和GPU

CPU:Central Processing Unit(中央處理器),中心處理器是計算機的大腦,每一個CPU核心會逐一執行不一樣任務如今不少計算機都是多芯片,多內核的。css

image

GPU:Graphics Processing Unit(圖形處理器),GPU擅長處理跨內核的簡單任務,是爲了解決圖形而開發的。在圖形環境中,「使用GPU支持」和「使用CPU」都與快速渲染與順滑渲染有關。html

image

許多帶扳手的GPU內核說明它們只能執行處理有限任務

當啓動電腦時候,是CPU和GPU爲應用供能。一般狀況下,應用是經過操做系統提供的機制在CPU和GPU上運行。html5

image

三層計算機繫結構:底部是機器硬件,中間是操做系統,頂層是應用程序。
在進程和線程上執行程序 進程(process)和線程(thread)

image

進程做爲邊界框,線程爲裏面抽象的魚在遊動

進程能夠被描述爲一個應用的執行程序,線程存在於進程並執行任意部分。web

啓動應用時,會建立一個或者多個進程來幫助應用工做,操做系統爲進程提供了一塊可使用的「內存」,應用的全部狀態都保存在該私有內存空間中,關閉應用,進程會關閉,操做系統釋放內存。跨域

image
image
image
image
image
image
image
image

進程能夠請求操做系統的另外一個進程來執行不一樣的任務。此時,會分配不一樣的內存給新進程,進程之間能夠經過(IPC:Inter Process Communication)進行通信。應用的某個工做進程失去響應,該進程就能夠在不中止應用程序其餘進程的狀況下,重啓。瀏覽器

image

獨立進程經過IPC通訊示意圖
瀏覽器架構

不一樣的瀏覽器多是多進程或者單進程的。緩存

image

Chrome瀏覽器架構,使用多進程的架構安全

瀏覽器頂層是瀏覽器進程(Browser process),與瀏覽器其餘應用模塊進行協調工做。 bash

image

Chrome瀏覽器各個進程的工做:服務器

  • Browser Process:
    • 地址欄,書籤欄,前進後退按鈕等信息
    • 不可見的一些操做,請求,文件訪問等
  • Rederer Process
    • 負責一個網頁tab的的全部工做
  • Plugin Process
    • 插件進程,處理一個網頁用到的插件,例如Flash的插件
  • GPU Process
    • 負責處理GPU相關的工做 *還有其餘進程,例如:擴展應用,應用進程

image

不一樣進程指向瀏覽器 UI 的不一樣部分

谷歌瀏覽器每個站點(網頁)都是獨立的進程,這樣能夠保證即便網頁崩潰了,其餘網頁也不會受到影響。瀏覽器有多個進程的好處就是安全性跟跟沙箱化。因爲操做系統提供了限制進程權限的方法,瀏覽器就能夠用沙箱保護某些進程。因爲進程有本身的私有內存空間,因此每一個進程都有公共基礎設施的拷貝,這也意味着會佔用更多的內存,當運行達到極限時候,Chrome對於同一站點的不一樣標籤頁,會使用同一進程。

image

當Chrome運行在強力硬件上時,會把不一樣的服務功能模塊分配到不一樣的進程,從而提高穩定性,可是當運行在弱硬件設備時候,會將一些服務功能模塊整合到同一個進程以節約內存,可是相應的穩定性也會降低。

image

每一個Iframe的渲染進程)——站點隔離

站點隔離實現每一個Iframe運行在獨立的渲染進程。每一個tab站點單獨運行一個進程,站點內的Iframe運行一個單獨的渲染進程,這樣在不一樣的站點內,相同Iframe能夠共享內存。

同源策略是web的安全模型,也就是某一站點在沒有受權的狀況下,其餘站點是不能獲取其數據的。進程隔離是分離站點的最高效手段。

image

站點隔離示意圖
導航時候發生了什麼?

它以瀏覽器進程(Browser Process)開始

瀏覽器進程管理一切除Ta外以外的一切,瀏覽器進程內有不少線程,例如繪製瀏覽器的按鈕和輸入欄的UI線程、處理網絡棧以從因特網獲取獲取的網絡線程、控制文件訪問的存儲線程。當輸入URL,輸入由瀏覽器的進程的UI線程處理。

image

頂部是瀏覽器 UI,底部是擁有 UI、網絡和存儲線程的瀏覽器進程圖
一個簡單的導航
  1. 處理輸入

瀏覽器進程的UI線程首先肯定是搜索查詢仍是一個URL。UI線程決定是搜索內容到搜索引擎仍是去一個網站。

image

UI 線程詢問輸入內容是搜索查詢仍是 URL 地址
  1. 當按下Enter鍵,UI線程啓用網絡去調取獲取站點內容,加載動畫會顯示在標籤頁的一角。網絡線程會經過適當的協議,爲請求創建TLS鏈接。

SSL(Secure Sockets Layer 安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是爲網絡通訊提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡鏈接進行加密。

SSL協議位於TCP/IP協議與各類應用層協議之間,爲數據通信提供安全支持。SSL協議可分爲兩層: SSL記錄協議(SSL Record Protocol):它創建在可靠的傳輸協議(如TCP)之上,爲高層協議提供數據封裝、壓縮、加密等基本功能的支持。

安全傳輸層協議(TLS)用於在兩個通訊應用程序之間提供保密性和數據完整性。該協議由兩層組成: TLS 記錄協議(TLS Record)和 TLS 握手協議(TLS Handshake)。較低的層爲 TLS 記錄協議,位於某個可靠的傳輸協議(例如 TCP)上面,與具體的應用無關,因此,通常把TLS協議歸爲傳輸層安全協議。

image

Roger[ˈrɑ:dʒə(r)]:無線通訊答語:收到。UI 線程告訴網絡線程要導航到 mysite.com

在這時,網絡線程可能會收到像 HTTP 301 那樣的服務器重定向頭。這種狀況下,網絡線程會告訴 UI 線程,服務器正在請求重定向。而後,另外一個 URL 請求會被啓動。

  1. 讀取響應

image

包含 Content-Type 的響應頭以及做爲實際數據的 payload

一旦開始收到響應主體,網絡線程會查看數據流的前幾個字節,響應報文的Content-Type字段會聲明數據的類型。但可能存在丟失會錯誤。因此有了MIME類型嗅探來解決這個問題。

若是響應是一個HTML文件,那麼下一步就會把數據傳給渲染進程,若是是一個下載的文件,意味着是一個下載請求,會把它傳遞給下載器管理器。

image

網絡線程詢問一個響應數據是不是從安全網站來的 HTML

此時也會進行safeBrowsing檢查,若是域名和數據匹配到一個惡意網站,那麼網絡線程會顯示一個警告頁面。此外也會進行Cross Origin Read Bloking (CORB)檢查,以確保敏感的跨域數據不被傳給渲染進程。

  1. 查找渲染進程

一旦全部的檢查處理完畢而且網絡線程肯定會導航到請求的站點,網絡請求線程會告訴UI線程全部的數據請求完畢。UI線程會尋找渲染進程開始渲染Web頁面。

image

網絡線程告訴 UI 線程去查找渲染進程

因爲網絡請求線程會花費時間,一次能夠應用一個優化措施,當UI線程正發送一個URL請求給網絡線程時候,能夠同時查找開啓一個渲染進程待命,若是導航重定向,這個渲染進程或許不會用到。

  1. 提交導航

如今數據和渲染進程就緒,瀏覽器會發送一個IPC(進程間通訊)到渲染進程去提交導航,他也會傳遞數據流,因此渲染進程會保持接受HTML數據,一旦瀏覽器進程收到渲染進程已經提交的確認信息,導航完畢而且文檔加載解析開始。

這時,地址欄已經更新,安全指示器和站點設置UI會放映新頁面的站點信息,此標籤頁的session歷史記錄被更新,因此前進後退按鈕會走向剛導航過的站點, 當你關閉標籤頁或者窗口,爲了優化Tab/session的還原,session歷史被保存在硬盤上。

image

瀏覽器和渲染進程間的 IPC,請求渲染頁面。
額外的步驟:初始加載完畢

一旦導航別提交,渲染進程開始加載資源和渲染頁面,一旦渲染進程渲染完畢,會發送IPC返回給瀏覽器進程,(這也會全部frameh和onload事件已經觸發和執行完畢後發生)。這時,UI線程中止標籤頁上的加載動畫。

image

導航到另外一個站點

簡單導航已經完成,在導航欄在輸入一個URL時,瀏覽器進程會先檢查已經渲染的站點是否關心beforeUnload事件,確認沒有的話,就會執行相同的操做,導航到另外一個站點。

befounload事件會在用戶離開或者關閉標籤頁面時候給予提醒「離開此站點」。

注意:不要添加無條件的beforeunload處理程序,會產生延時。

image

瀏覽器進程向渲染進程發送 IPC 告訴它將要導航到另外一個站點

若是渲染進程啓動了導航(window.location.herf=xxx),渲染進程會先檢查befoeload事件處理程序,會像瀏覽器處理導航同樣執行相同的步驟,惟一不一樣的是導航請求是由渲染進程發送到瀏覽器進程的。

當新導航到新站點時,會調用一個獨立的渲染進程來處理導航,同時保留當前的渲染進程來處理unload事件。

image

2 個 IPC(從瀏覽器進程到新渲染進程)告知渲染頁面並告知舊渲染進程卸載
Service Worker待補充

渲染進程的內部機制

渲染進程處理網站內容

渲染進程負責標籤內發送的全部事情。渲染進程中,主線程處理服務器返回給用戶的大部分數據,若是使用了web sworker或Service Sorker,部分JS將由工做線程處理。合成和光柵線程也在渲染進程內運行,以高效流暢的呈現頁面。

渲染進程的核心工做是將HTML,CSS和JavaScript轉換爲用戶能夠與之交互的網頁。

image

渲染進程內部包含主線程、工做線程、合成線程和光柵線程
  1. 解析(Parsing)
  • Dom的構建 當渲染進程收到HTML數據時,主線程解析文本字符串(HTML)並將其轉換爲(DOM). DOM是頁面在瀏覽器的內部表現,也是開發人員經過JS與之交互的數據結構

HTML的標籤的解析由HTML Standar決定,HTML規範能夠很優雅的處理一些標籤錯誤。

  1. 子資源加載

網站的圖像,CSS,JS外部資源,須要從網路或者緩存加載。解析構建DOM時,主線程會按處理順序逐個加載,爲了加快速度,「預加載掃描器(preload scanner)」會同時進行。若是文檔有<img><link>,預加載掃描器會在瀏覽器進程中發送請求。

image

主線程解析 HTML 並構建 DOM 樹

3.JS阻塞解析 當解析HTML時,遇到<script>時,會中止解析接下來的內容,加載js,並執行裏面的代碼。

  1. 提示瀏覽器如何加載資源

能夠在<script>標籤加async或者defer屬性,實現異步加載JS,或者加載JS模塊,可使用 <link rel="preload">告知瀏覽器當前導航確定須要該資源,而且你但願儘快下載。

  1. 樣式計算

只有DOM元素沒法肯定頁面的外觀,須要結合CSS樣式。主線程解析CSS而且肯定每一個DOM節點計算後的樣式。

image

主線程解析 CSS 以添加計算後樣式
  1. 佈局樹(Layout Tree)

知道每一個節點已經對應的元素樣式,不足以渲染頁面。佈局是計算幾何元素形狀的過程,主線程遍歷DOM,計算樣式並建立佈局樹,其中包含xy座標和邊框大小等信息,佈局樹可能與DOM樹結構樹相似,但它僅包含頁面可見內容的信息。若是一個元素應用了 display:none,那麼該元素不是佈局樹的一部分。相似地,若是應用瞭如 p::before{content:"Hi!"} 的僞類,則即便它不在 DOM 中,也包含於佈局樹中。

image

主線程遍歷計算樣式後的 DOM 樹,以今生成佈局樹
  1. 繪製
    image

有了DOM、樣式、佈局樹還不能畫出頁面,還不知道繪製的順序。 例如,有的元素有了z-index,簡單從上到下繪製就會出現圖層高低錯誤。

image

由於沒有考慮z-index,頁面元素按HTML標記的順序出現,致使錯誤的渲染圖像

在繪製步驟,主線程遍歷佈局樹(Layout Tree)建立繪製記錄(Paint Records),就像是背景優先,而後矩形,文字。

image

線程遍歷佈局樹並生成繪製記錄
  • 更新渲染管道的成本很高
    image
DOM + Style、佈局和繪製樹的生成順序

渲染管道最重要的事情:每一個步驟,都是前一個操做的結果用於後一個操做的數據。若是爲元素設置動畫,每一幀都要進行相同的處理操做,大多數瀏覽器的1幀/秒 若是瀏覽器丟失了中間部分幀,就會讓人以爲卡頓。

image

時間軸上的動畫幀
  1. 真正繪製一個頁面
  • 光柵化
    image
簡單光柵處理示意圖

如今知道了文檔的結構,CSS,佈局樹,繪製順序。就是將數據轉化爲物理設備上的像素了。這個過程成爲光柵化。

  • 合成
    image

合成處理是將頁面的各個部分光柵化,而且合成線程進行圖層移動合成。

  • 分層

爲了分清哪些元素位於什麼圖層,主線程遍歷佈局樹建立圖層樹,若是某些部分是單獨圖層(例如劃入式側面菜單欄),但沒有拆分出來,能夠用CSS屬性:will-change提示瀏覽器。

image

  • 主線程的光柵化和合成

一旦建立了圖層樹和肯定了繪製順序,主線程將會把信息傳遞給合成線程,接着,合成線程會光柵化每一個圖層,一個圖層可能跟頁面同樣大,合成線程將其分塊後發送給光柵線程。光柵線程光柵化每一個小塊後將他們存儲在顯存中。

image

光柵線程建立分塊的位圖併發送到 GPU

合成線程會不一樣的光柵化線程設置優先級,以便視圖或者附近區域的畫面能夠先光柵化顯示。圖層還具備不一樣的分辨率的塊,能夠放大顯示。

一旦塊被光柵化,合成線程會收集這些塊的信息(稱爲繪製四邊形),建立合成幀。

  • 繪製四邊形:包含塊在內存的位置,以及合成時塊在頁面中的位置等信息。
  • 合成幀:一個繪製四邊形的集合,表明一個頁面的一幀。

接着,合成幀經過IPC提交給瀏覽器進程,此時,能夠在UI線程或者其餘插件的渲染進程添加一個合成幀,這些合成器幀被送到GPU而後在屏幕上顯示。若是收到滾動事件,合成幀會建立另外一個合成幀到GPU。

合成線程建立合成幀,將其發送到瀏覽器進程,再接着發送到 GPU

image

用戶輸入行爲與合成器

用戶的任何行爲,對於瀏覽器來講都是輸入行爲。包括點擊,滾動,觸摸屏幕,滑動鼠標。

例如用戶觸摸屏幕時,瀏覽器進程率先捕捉行爲,瀏覽器進程所掌握的信息僅限於行爲發生的區域,由於標籤內的內容都由渲染進程處理。瀏覽器進程會將點擊行爲以及座標傳達給渲染進程。渲染進程在進行相應的處理。

image

合成器接收輸入事件

image

懸於頁面圖層的視圖窗口
理解非當即可滾動區

運行JS是渲染進程的主線程的工做,頁面合成以後,註冊了事件的區域叫作「非當即可滾動區」,合成器線程會通知渲染進程的主線程處理。沒有輸入事件沒有發生在事件註冊區域,合成器進程則不須要等待主線程,能夠繼續合成幀。

image

設置時間處理應該注意
document.body.addEventListener('touchstart', 
event => {
    if (event.target === area) {
        event.preventDefault();
    }
});
複製代碼

事件代理是瀏覽器經常使用的事件處理模式,在頂層元素添加一個事件。 這樣帶來的問題就是整個頁面都被標識爲非當即滾動區域,合成器進程須要每次都詢問主線程是否須要處理事件而且等待反饋。流暢的合成器處理模式就失效了。

image

你能夠給事件監聽添加一個 passive:true 選項 ,將這種負面效果最小化。這會提示瀏覽器你想繼續在主線程中監聽事件,但合成器沒必要停滯等候,可接着建立新的合成幀。

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});
複製代碼

不過上述寫法可能又會帶來另一個問題,假設某個區域你只想要水平滾動,使用 passive: true 能夠實現平滑滾動,可是垂直方向的滾動可能會先於event.preventDefault()發生,此時能夠經過 event.cancelable 來防止這種狀況。

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // 阻止默認的滾動行爲
        /*
        *  這裏設置程序執行任務
        */
    } 
}, {passive:: true});
複製代碼

也可使用css屬性 touch-action 來徹底消除事件處理器的影響,如:#area{touch-action:pan-x;}

查找事件對象

當組合器線程發送輸入事件給主線程時,主線程首先會進行命中測試(hit test),來查找對應的時間目標,命中測試會基於渲染過程當中生成的繪製記錄(paint records)查找事件發生座標下尋找的元素。

image

主線程檢查繪製記錄查詢座標 x、y 處繪製內容

#####事件的優化

通常咱們屏幕的刷新速率爲 60fps,可是某些事件的觸發量會不止這個值,出於優化的目的,Chrome 會合並連續的事件(如 wheel, mousewheel, mousemove, pointermove, touchmove ),並延遲到下一幀渲染時候執行 。而如 keydown, keyup, mouseup, mousedown, touchstart, 和 touchend 等非連續性事件則會當即被觸發。

image

使用 getCoalescedEvents 獲取幀內事件

事件合併可幫助大多數 web 應用構建良好的用戶體驗。然而,若是你開發的是一個繪圖類應用,須要基於 touchmove 事件的座標繪製線路,那麼在你試圖畫下一根光滑的線條時,區間內的一些座標點也可能會因事件合併而丟失。這時,你可使用目標事件的 getCoalescedEvents 方法獲取事件合併後的信息。

image

window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // 使用 x、y 座標畫線
    }
});
複製代碼

參考:

相關文章
相關標籤/搜索