中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】

發布時間:2020-08-08 11:07:47 來源:ITPUB博客 閱讀:248 作者:智云編程 欄目:web開發

Chrome 算是程序員的標配了,從全球的市場份額來看,它在全球市場的份額已經超過 60%。

在 Chrome 10 周年之際,官方發布了一個系列文章,用圖解的方式,很清晰的講解了現代瀏覽器的運行原理。

渲染器進程涉及到 Web 性能相關的多個方面,由于渲染器進程中處理了很多的邏輯,不是一篇文章可以全面講解的,因此本文僅作為一個概述。如果你有興趣深入研究,可以在《Why Performance Matters》這篇文章里找到更多的資料。

渲染器進程處理Web內容

所有選項卡內發生的邏輯,都由渲染器進程負責。在渲染器進程中,主線程處理了服務器發送給用戶的大部分代碼。如果你使用到 Web Workder 或者Service Worker,那 JavaScript 中的這部分代碼,將由工作線程處理。Compositor(合成器) 和 Raster(光柵) 線程也在渲染器內運行,從而實現高效、流暢的渲染頁面。

渲染器進程的核心工作是將 HTML,CSS 和 JavaScript 轉換為用戶可以與之交互的網頁。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

上圖中,描述了具有主線程、工作線程、Compositor 線程、Raster 線程的渲染器進程,以及他們之間的關系。

解析

構建 DOM

當渲染器進程收到一個導航請求,并開始接收 HTML 數據,主線程將開始處理文本字符串(HTML),將其解析成 DOM(Document Object Model)。

DOM 是 Web 頁面的內部的邏輯樹文檔結構,Web 開發人員可以通過 JavaScript 腳本與之交互數據,以及通過標準 API 來操作 DOM 節點。

將 HTML 文檔解析成 DOM 是完全依照于 HTML 協議。并且在 HTML 協議中,瀏覽器不會對錯誤的 HTML 進行錯誤提示。例如,缺少結束的  </p> 標簽時,這依然是一個有效的 HTML。類似 Hi! <b>I'm <i>Chrome</b>!</i>  中, b 標簽在 i 標簽之前關閉這樣的錯誤,會被 HTML 理解為 Hi! <b>I'm <i>Chrome</i></b><i>!</i>  。這是因為 HTML 規范的主要原則是優雅的處理這些錯誤,而不是嚴格檢查。

如果你對這些規范感到好奇,可以閱讀 HTML 規范中的 “解析器中的錯誤處理和奇怪案例介紹” 部分。

解析器中的錯誤處理和奇怪案例介紹:

https://html.spec.whatwg.org/multipage/parsing.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser

子資源加載

一個完整的 Web 站點通常會包含圖片、CSS 和 JS 等外部資源,這些文件都需要從網絡或者本地緩存中加載。主線程可以在解析構建 DOM 的時候,將他們逐個請求,但是為了加快速度,會同時使用 “預加載掃描(Preload Scanner)”。

如果 “預加載掃描” 發現有類似 <img>  或 <link>  這樣的標簽時,會由 HTML 解析器對該資源生成一個 Tokens,然后在瀏覽器進程中,通過網絡或者本地緩存來加載資源。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

上圖描述了,主線程解析 HTML 并構建 DOM 樹的過程。

自己是一個五年的前端工程師


這里推薦一下我的前端學習交流群:731771211,里面都是學習前端的,如果你想制作酷炫的網頁,想學習編程。從最基礎的HTML+CSS+JS【炫酷特效,游戲,插件封裝,設計模式】到移動端HTML5的項目實戰的學習資料都有整理,送給每一位前端小伙伴,有想學習web前端的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小伙伴歡迎加入。 點擊:  加入

JS 可以阻止解析

當 HTML 解析器遇到 <script> 標簽的時候,它會暫停解析 HTML 文檔,然后對這個 JS 腳本進行加載、解析和執行。

這么設計的原因,是因為 JS 可以使用類似 document.write() 方法來改變 DOM 的結構。這就是 HTML 解析器在重新解析 HTML 之前,必須等待 JS 腳本執行的原因。

如果你對 JS 執行中發生的事情細節有興趣,V8 團隊有一篇文章深入的對此進行了講解,有興趣可以看看。

V8 團隊深入研究:
https://mathiasbynens.be/notes/shapes-ics

提示瀏覽器如何加載資源

HTML 遇到 JS 腳本則暫停對 HTML 的解析,這并不是絕對的。

Web 開發人員可以通過多種方式的配置,告知瀏覽器如何更優雅的加載資源。如果你的 JS 腳本中,沒有使用到類似 document.write() 這樣的方法,你可以在  script 標簽中添加  async  或 defer 標記,然后瀏覽器會異步加載和運行此 JS 腳本,不會阻斷解析。如果需要,也可以使用 JavaScript Modules,還可以通過  <link rel="preload">  標簽向瀏覽器明確標記此為重要的資源,將在頁面加載完成之后被立刻使用,對于這類資源,它會在頁面加載生命周期的早期,被優先加載。

樣式渲染(Style)

僅僅解析成 DOM,還不足以完成頁面渲染,因為還可以通過在 CSS 中,設置元素的樣式來豐富渲染效果。

主線程將解析 CSS,并將效果渲染到指定的 DOM 節點上,關于 CSS 選擇器如何定位到指定的 DOM 節點,可以通過 DevTools 來查看相關信息。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

上圖中,主線程解析 CSS 并添加渲染樣式。

即使你不使用任何 CSS 樣式,每個 DOM 節點依然存在默認的渲染樣式。例如, h2 標簽在視覺上就大于 h3 標簽,并且每個元素還有默認的邊距。這是因為瀏覽器具有默認樣式表。

如果你對 Chrome 的默認 CSS 是什么樣的有興趣,可以在源碼中看到具體細節。

Chrome 的默認 CSS:
https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/css/html.css

布局(Layout)

到現在,渲染器進程知道每個 DOM 的結構和樣式了,但是這依然不足以渲染頁面。想象一下,你正視圖通過文字向朋友描述一副畫,“有一個大的紅色圓圈和一個小的藍色方塊”,這些信息不足以讓你的朋友還原這幅畫。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

這就牽扯到布局(Layout),布局是對元素定位的過程,主線程遍歷 DOM 并計算樣式,然后創建布局樹(Layout Tree),在布局樹中,包含 X、Y 坐標和邊框大小等信息。布局樹是一個與 DOM 樹類似的結構,但是它僅僅包含了頁面上可見內容相關的信息。

舉個例子,如果某個元素設置了 display:none ,則該元素將不會出現在布局樹中,但是它會出現在 DOM 樹中,而如果該元素被設置為  visibility:hidden  則它會存在于布局樹中。類似的例子還有  p::before{content:"Hi!"} 這樣的偽類,它會存在于布局樹中,而不會存在于 DOM 樹中。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如上圖所示,在主線程中渲染樣式,并生成布局樹和 DOM 樹。

計算頁面布局是一個很復雜的工作,即使最簡單的從上到下的塊流結構,也必須考慮字體的大小以及如何劃分每一塊,因為它們會影響當前段落的大小和形狀,然后影響下一塊所在的位置。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

CSS 樣式可以設置元素浮動到某一側、隱藏 overflow 的元素,或者改變排版方向。布局是一個非常復雜的工作,在 Chrome 中,有一個完整的工程師團隊負責布局。如果你的對他們工作的細節感興趣,可以參閱 BinkOn 會議的記錄。

BinkOn:
https://www.youtube.com/watch?v=Y5Xa4H2wtVA

繪制(Paint)

擁有 DOM、CSS 和 LayoutTree 仍然不足以渲染頁面。假設你正在嘗試重繪一幅畫,你除了需要知道元素的大小、外觀和位置之外,還需要知道它們的繪制順序。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

例如: z-index 屬性將改變元素的層級,在這種情況下,按 HTML 中編寫的元素順序進行繪制,將導致渲染結果和預期不符。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如上圖所示,因為沒有正確的考慮 z-index ,將導致頁面被錯誤的渲染。

在這個繪制的過程中,主線程遍歷布局樹,然后創建繪制記錄。繪制記錄是一個繪制過程的注釋,例如“背景優先,然后是文本,最后是矩形”。如果你曾經使用 JS 在  <canvas> 上繪制元素,那么你對此過程應該會很熟悉。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如上圖所示,主線程遍歷布局樹,并生成繪制記錄。

更新渲染管道的成本很高

渲染管道(Rendering Pipeline)中最重要的任務,就是在每個步驟開始前,根據前一次操作的結果,來創建新的數據。例如,如果布局樹中的某些內容發生更改,則需要為文檔的受影響部分重新生成“繪制”順序。

渲染管道(Rendering Pipeline)中最重要的任務,就是在每個步驟開始前,根據前一次操作的結果,來創建新的數據。例如,如果布局樹中的某些內容發生變動,則需要為文檔中受影響的部分,重新生成“繪制記錄”。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

為元素設置的動畫,瀏覽器必須在每一幀之間執行這些操作。我們大多數顯示器每秒刷新 60 次(60fps),如果你對每一幀都做了處理,那動畫對人眼而言就是平滑的,但是如果某些幀沒有被處理到或者丟失了,則會導致動畫不連貫,出現頁面的“卡頓”。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

哪怕渲染的計算可以跟上屏幕刷新,可因為此計算過程發生在主線程上,當執行 JavaScript 腳本時,可能導致渲染過程被阻斷。

即使渲染的計算可以跟上屏幕的刷新速度,可因為此計算是在主線程上執行的,這就意味著 JS 代碼的執行,也可能導致它被阻斷。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如上圖,時間軸上的動畫幀,被 JS 阻止了一幀。

為此,你可以將 JavaScript 操作劃分成小塊,并在每幀上執行 requestAnimationFrame() ,還可以在 Web Workers 中運行 JavaScript,以避免阻塞主線程。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如圖所示,在動畫幀的時間軸上,運行較小的 JavaScript 塊。

合成(Compositing)

如何繪制一個頁面?

現在瀏覽器知道文檔的結構,每個元素的樣式,頁面的形狀和繪制順序,它是如何繪制頁面的?將此信息轉換為屏幕上的像素稱為光柵化(rasterizing)。

光柵化是將幾何數據經過一系列變換后最終轉換為像素,從而呈現在顯示設備上的過程。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
305.gif

也許處理這種情況的一種無腦方案,是在視口(ViewPort)內部將每個組件都光柵化。如果用戶滾動頁面,則移動光柵幀,并通過更多光柵元素填充缺少的部分。

這就是 Chrome 首次發布時處理光柵化的方式,但是,現代瀏覽器運行一個更復雜的被稱為合成(Compositing)的進程。

什么是合成(Compositing)

合成是一種將頁面的各個元素進行分層,分別光柵化,并在合成器線程中以一個單獨的線程合成新頁面的技術。如果頁面發生滾動,由于圖層已經光柵化,因此它需要做的就是合成一個新幀。通過移動圖層同時合成新幀,可以以相同的方式實現動畫。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】

你可以在 DevTools 中的 Layout panel 來查看看圖層。

分層

為了確定每個元素所在的層,主線程遍歷布局樹以創建層樹(Layer Tree)。如果頁面的某元素應該是一個單獨的圖層(例如側滑菜單),那么你可以在 CSS 中,使用 will-change 屬性提示瀏覽器。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

如上圖,在主線程中遍歷布局樹,并生成層樹。

雖然理想情況下,應該為每個元素生成圖層,但是對過多的小圖層進行合并,可能會比對頁面的每幀上柵格化小元素更慢,因此測量應用程序的渲染性能就非常重要。有關主題的更多信息,請參閱 Stick to Compositor-Only Properties 和 Manage Layer Count。

Stick to Compositor-Only Properties 和 Manage Layer Count:
https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count

光柵和合成,脫離主線程

一旦創建了層樹并確定了繪制順序,主線程就會將該信息提交給合成器線程。合成器線程會光柵化每個圖層,一個圖層可能想一個完整的頁面那么大,因此合成器線程將他們分成圖塊,并將每個圖塊發送到光柵線程。光柵線程格式化每個元素,并將他們存儲在 GPU 內存中。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
光柵線程創建光柵位圖并發送到GPU

合成器線程可以優先考慮不同的光柵線程,以便 ViewPort(或附近)的元素可以被優先光柵化。圖層還具有多個不同分辨率的傾斜度,以便對內容的放大等操作。

一旦元素被光柵化,合成器線程會收集被稱為 “繪制矩形(Draw Quads)” 的信息,用以創建一個合成幀(Compositor Frame)。

然后通過 IPC 將合成幀提交給瀏覽器進程。此時,可以從 UI 線程添加另一個合成幀用于瀏覽器的 UI 更新,或者從其他渲染器進程中添加擴展。這些合成幀被發送到 GPU 中,用以在屏幕上顯示。如果觸發滾動事件,合成器線程會創建另一個合成幀發送到 GPU。

HTML/CSS/JS 是如何在瀏覽器中渲染成你看到的頁面?【圖解Chrome】
image.png

上圖中,合成器線程創建合成幀。將此幀發送到瀏覽器進程然后發送到 GPU。

合成(Compositor)的好處,是它可以在不影響主線程的情況下完成。合成器線程不需要等待樣式計算或者 JS 腳本執行,這就是為什么 “僅合成動畫” 被認為是平滑性能的最佳選擇。如果需要再次計算不會或者重新繪制,則必須涉及到主線程。

小結

在這篇文章中,我們研究了從解析到合成的渲染流程,更多關于網站優化問題可以關注一下。

自己是一個五年的前端工程師


這里推薦一下我的前端學習交流群:731771211,里面都是學習前端的,如果你想制作酷炫的網頁,想學習編程。從最基礎的HTML+CSS+JS【炫酷特效,游戲,插件封裝,設計模式】到移動端HTML5的項目實戰的學習資料都有整理,送給每一位前端小伙伴,有想學習web前端的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小伙伴歡迎加入。 點擊:  加入


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

建宁县| 共和县| 奉节县| 巨野县| 黄梅县| 塔河县| 贺兰县| 仙桃市| 开鲁县| 垫江县| 霍邱县| 栾城县| 湖北省| 嘉黎县| 乌审旗| 滨海县| 东明县| 固镇县| 枣阳市| 吴川市| 双牌县| 金溪县| 五大连池市| 五河县| 行唐县| 安康市| 犍为县| 宜昌市| 海门市| 元朗区| 屯门区| 阳新县| 望都县| 玛沁县| 长汀县| 高碑店市| 阳原县| 延寿县| 望谟县| 奉化市| 贵南县|