瀏覽器頁(yè)面渲染機(jī)制

發(fā)送 & 接收信息

數(shù)據(jù)是以“數(shù)據(jù)包”的形式通過(guò)互聯(lián)網(wǎng)發(fā)送肤晓,而數(shù)據(jù)包以字節(jié)為單位。當(dāng)你編寫一些 HTML认然、CSS 和 JS补憾,并試圖在瀏覽器中打開(kāi) HTML 文件時(shí),瀏覽器會(huì)從你的硬盤(或網(wǎng)絡(luò))中讀取 HTML 的原始字節(jié)卷员。明白了嗎盈匾?瀏覽器讀取的是原始數(shù)據(jù)字節(jié),而不是你編寫的代碼的實(shí)際字符毕骡。

讓我們繼續(xù)削饵。瀏覽器接收字節(jié)數(shù)據(jù),但是未巫,它用這些數(shù)據(jù)什么都做不了窿撬。數(shù)據(jù)的原始字節(jié)必須轉(zhuǎn)換為它所理解的形式,這是第一步橱赠。

從 HTML 的原始字節(jié)到 DOM

瀏覽器對(duì)象需要處理的是文檔對(duì)象模型(DOM)對(duì)象尤仍。那么,DOM 對(duì)象是從何而來(lái)的呢狭姨?這很簡(jiǎn)單宰啦。首先,將原始數(shù)據(jù)字節(jié)轉(zhuǎn)換為字符饼拍。這一點(diǎn)赡模,你可以通過(guò)你所編寫的代碼的字符看到。這種轉(zhuǎn)換是基于 HTML 文件的字符編碼完成的师抄。至此漓柑,瀏覽器已經(jīng)從原始數(shù)據(jù)字節(jié)轉(zhuǎn)換為文件中的實(shí)際字符。但這不是最終的結(jié)果。這些字符會(huì)被進(jìn)一步解析為一些稱為“標(biāo)記(token)”的東西

那么辆布,這些標(biāo)記是什么瞬矩?文本文件中的一堆字符對(duì)瀏覽器引擎而言沒(méi)什么用處。如果沒(méi)有這個(gè)標(biāo)記化過(guò)程锋玲,那么這一堆堆字符只會(huì)生成一系列毫無(wú)意義的文本景用,即 HTML 代碼——不會(huì)生成一個(gè)真正的網(wǎng)站。

當(dāng)你保存一個(gè)擴(kuò)展名為.html 的文件時(shí)惭蹂,就向?yàn)g覽器引擎發(fā)出了把文件解析為 HTML 文檔的信號(hào)伞插。瀏覽器“解釋”這個(gè)文件的方式是首先解析它。在解析過(guò)程中盾碗,特別是在標(biāo)記化過(guò)程中媚污,瀏覽器會(huì)解析 HTML 文件中的每個(gè)開(kāi)始和結(jié)束“標(biāo)簽(tag)”。解析器可以識(shí)別尖括號(hào)中的每個(gè)字符串廷雅,如“< html>”耗美、“< p>”,也可以推斷出適用于其中任何一個(gè)字符串的規(guī)則集榜轿。例如幽歼,表示錨標(biāo)簽的標(biāo)記與表示段落標(biāo)簽的標(biāo)記具有不同的屬性。

從概念上講谬盐,你可以將標(biāo)記看作某種數(shù)據(jù)結(jié)構(gòu),它包含關(guān)于某個(gè) HTML 標(biāo)簽的信息诚些。本質(zhì)上飞傀,HTML 文件會(huì)被分解成稱為標(biāo)記的小的解析單元。瀏覽器就是這樣開(kāi)始識(shí)別你所編寫的內(nèi)容的诬烹。

但標(biāo)記還不是最終的結(jié)果砸烦。標(biāo)記化完成后,接下來(lái)绞吁,標(biāo)記將被轉(zhuǎn)換為節(jié)點(diǎn)幢痘。你可以將節(jié)點(diǎn)看作是具有特定屬性的不同對(duì)象。實(shí)際上家破,更好的解釋是颜说,將節(jié)點(diǎn)看作是文檔對(duì)象樹(shù)中的獨(dú)立實(shí)體。但節(jié)點(diǎn)仍然不是最終結(jié)果汰聋。

現(xiàn)在门粪,讓我們看一下最后一點(diǎn)。在創(chuàng)建好之后烹困,這些節(jié)點(diǎn)將被鏈接到稱為 DOM 的樹(shù)數(shù)據(jù)結(jié)構(gòu)中玄妈。DOM 建立起了父子關(guān)系、相鄰兄弟關(guān)系等。在這個(gè) DOM 對(duì)象中拟蜻,每個(gè)節(jié)點(diǎn)之間都建立了關(guān)系∫锴現(xiàn)在,這是我們可以使用的東西了酝锅。

Bytes=> characters=>Tokens=>Node=>Dom

字節(jié)=>字符=>標(biāo)記=>節(jié)點(diǎn)=>Dom樹(shù)

根據(jù) HTML 文件的大小辜御,DOM 的構(gòu)建過(guò)程可能需要一些時(shí)間。無(wú)論文件多小屈张,它都需要一些時(shí)間擒权。

CSS 如何轉(zhuǎn)換?

DOM 已經(jīng)創(chuàng)建阁谆。帶有一些 CSS 的典型 HTML 文件會(huì)包含下面這樣的樣式表鏈接:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
</head>
<body>

</body>
</html>

當(dāng)瀏覽器接收到原始數(shù)據(jù)字節(jié)并啟動(dòng) DOM 構(gòu)建過(guò)程時(shí)碳抄,它還會(huì)發(fā)出請(qǐng)求來(lái)獲取鏈接的 main.css 樣式表。當(dāng)瀏覽器開(kāi)始解析 HTML 時(shí)场绿,在找到 css 文件的鏈接標(biāo)簽的同時(shí)剖效,它會(huì)發(fā)出請(qǐng)求來(lái)獲取它⊙娴粒可能你已經(jīng)猜到璧尸,瀏覽器還是接收 CSS 數(shù)據(jù)的原始字節(jié),從互聯(lián)網(wǎng)或是本地磁盤熬拒。

但是爷光,瀏覽器如何處理這些 CSS 數(shù)據(jù)的原始字節(jié)呢?

從 CSS 的原始字節(jié)到 CSSOM

當(dāng)瀏覽器接收到 CSS 的原始字節(jié)時(shí)澎粟,會(huì)啟動(dòng)一個(gè)和處理 HTML 原始字節(jié)類似的過(guò)程蛀序。就是說(shuō),原始數(shù)據(jù)字節(jié)被轉(zhuǎn)換成字符活烙,然后標(biāo)記徐裸,然后形成節(jié)點(diǎn),最后形成樹(shù)結(jié)構(gòu)啸盏。

什么是樹(shù)結(jié)構(gòu)重贺?大多數(shù)人都知道 DOM 這個(gè)詞。同樣回懦,也有一種 CSS 樹(shù)結(jié)構(gòu)气笙,稱為 CSS 對(duì)象模型,簡(jiǎn)稱為 CSSOM粉怕。

你知道健民,瀏覽器不能使用 HTML 或 CSS 的原始字節(jié)。必須將其轉(zhuǎn)換成它能識(shí)別的形式贫贝,也就是這些樹(shù)形結(jié)構(gòu)秉犹。

CSS 有一個(gè)叫做級(jí)聯(lián)的東西蛉谜。級(jí)聯(lián)是瀏覽器確定如何在元素上應(yīng)用樣式的機(jī)制。

由于影響元素的樣式可能來(lái)自父元素崇堵,即通過(guò)繼承型诚,或者已經(jīng)在元素本身設(shè)置,所以 CSSOM 樹(shù)結(jié)構(gòu)變得很重要鸳劳。為什么狰贯?這是因?yàn)?strong>瀏覽器必須遞歸遍歷 CSS 樹(shù)結(jié)構(gòu)并確定影響特定元素的樣式。

一切順利赏廓。瀏覽器有了 DOM 和 CSSOM 對(duì)象『桑現(xiàn)在,我們能在屏幕上呈現(xiàn)一些東西了嗎幔摸?

渲染樹(shù)

我們現(xiàn)在得到的是兩個(gè)獨(dú)立的樹(shù)結(jié)構(gòu)摸柄,它們似乎沒(méi)有共同的目標(biāo)。

DOM 和 CSSOM 樹(shù)結(jié)構(gòu)是兩個(gè)獨(dú)立的樹(shù)結(jié)構(gòu)既忆。DOM 中包含關(guān)于頁(yè)面 HTML 元素關(guān)系的所有信息驱负,而 CSSOM 則包含關(guān)于元素樣式的信息。好了患雇,瀏覽器現(xiàn)在把 DOM 和 CSSOM 樹(shù)組合成一棵渲染樹(shù)跃脊。

DOM + CSSOM = 渲染樹(shù)

渲染樹(shù)包含頁(yè)面上所有關(guān)于可見(jiàn) DOM 內(nèi)容的信息以及不同節(jié)點(diǎn)所需的所有 CSSOM 信息。注意苛吱,如果一個(gè)元素被 CSS 隱藏酪术,例如使用 display; none,那么節(jié)點(diǎn)就不會(huì)包含在渲染樹(shù)中又谋。隱藏元素會(huì)出現(xiàn)在 DOM 中拼缝,但不會(huì)出現(xiàn)在渲染樹(shù)中。這是因?yàn)?strong>渲染樹(shù)結(jié)合了來(lái)自 DOM 和 CSSOM 的信息彰亥,所以它知道不能把隱藏元素包含在樹(shù)中。

構(gòu)建好渲染樹(shù)之后衰齐,瀏覽器將繼續(xù)下個(gè)步驟:布局任斋!

布局

現(xiàn)在,我們有了屏幕上的內(nèi)容和所有可見(jiàn)內(nèi)容的樣式信息——但是我們并沒(méi)有實(shí)際在屏幕上渲染任何內(nèi)容耻涛。首先废酷,瀏覽器必須計(jì)算頁(yè)面上每個(gè)對(duì)象的確切大小和位置。這就好比是抹缕,把要在頁(yè)面上渲染的所有元素的內(nèi)容和樣式信息傳遞給一個(gè)有才華的數(shù)學(xué)家澈蟆。然后,這位數(shù)學(xué)家用瀏覽器的視窗計(jì)算出每個(gè)元素的確切位置和大小卓研。

這個(gè)布局步驟考慮了從 DOM 和 CSSOM 接收到的內(nèi)容和樣式趴俘,并執(zhí)行了所有必要的布局計(jì)算睹簇。有時(shí),你會(huì)聽(tīng)到人們把這個(gè)“布局”階段稱為“回流(reflow)

藝術(shù)家出場(chǎng)

現(xiàn)在寥闪,每個(gè)元素的確切位置已經(jīng)計(jì)算出來(lái)太惠,剩下的就是將元素“繪制”到屏幕上。

考慮一下疲憋。我們已經(jīng)得到了在屏幕上顯示元素所需的所有信息凿渊。我們只要把它展示給用戶。這就是這個(gè)階段的全部工作缚柳。有了元素內(nèi)容(DOM)埃脏、樣式(CSSOM)計(jì)算得出的元素的精確布局信息,瀏覽器現(xiàn)在就可以將節(jié)點(diǎn)逐個(gè)“繪制”到屏幕上了秋忙。元素現(xiàn)在終于呈現(xiàn)在屏幕上了彩掐!

渲染阻塞資源

當(dāng)你聽(tīng)到“渲染阻塞(render-blocking)”時(shí),你會(huì)想到什么翰绊?我猜你想的是佩谷,“有東西阻止了屏幕上節(jié)點(diǎn)的實(shí)際繪制”。如果你這么說(shuō)监嗜,那你說(shuō)的完全正確谐檀!

優(yōu)化網(wǎng)站的第一準(zhǔn)則是讓最重要的 HTML 和 CSS 盡可能快地傳遞到客戶端。在成功繪制之前裁奇,必須構(gòu)造 DOM 和 CSSOM桐猬,因此,HTML 和 CSS 都是渲染阻塞資源刽肠。關(guān)鍵是溃肪,你應(yīng)該盡快將 HTML 和 CSS 提供給客戶端,以優(yōu)化應(yīng)用程序的首次渲染時(shí)間音五。

JavaScript 如何執(zhí)行惫撰?

一個(gè)好的 Web 應(yīng)用程序肯定會(huì)使用一些 JavaScript。這是一定的躺涝。JavaScript 的“問(wèn)題”在于你可以使用 JavaScript 修改頁(yè)面的內(nèi)容和樣式厨钻。通過(guò)這種方式,你可以從 DOM 樹(shù)中刪除元素和添加元素坚嗜,還可以通過(guò) JavaScript 修改元素的 CSSOM 屬性夯膀。

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>

這是一個(gè)非常簡(jiǎn)單的文檔。樣式表 style.css 只有下面一個(gè)聲明語(yǔ)句:

body {
  background: #8cacea;
}

根據(jù)前面的解釋苍蔬,瀏覽器從磁盤(或網(wǎng)絡(luò))讀取 HTML 文件的原始字節(jié)并將其轉(zhuǎn)換為字符诱建。字符被進(jìn)一步解析為標(biāo)記。當(dāng)解析器遇到< link rel="stylesheet" href="style.css">時(shí)碟绑,就會(huì)請(qǐng)求獲取 CSS 文件 style.css俺猿。DOM 構(gòu)造繼續(xù)進(jìn)行茎匠,當(dāng) CSS 文件返回一些內(nèi)容后,CSSOM 構(gòu)造就開(kāi)始了辜荠。

引入 JavaScript 后汽抚,這個(gè)過(guò)程會(huì)發(fā)生什么變化?要記住伯病,其中最重要的一件事情是造烁,每當(dāng)瀏覽器遇到腳本標(biāo)簽時(shí),DOM 構(gòu)造就會(huì)暫停午笛!整個(gè) DOM 構(gòu)建過(guò)程都將停止惭蟋,直到腳本執(zhí)行完成

這是因?yàn)?JavaScript 可以同時(shí)修改 DOM 和 CSSOM药磺。由于瀏覽器不確定特定的 JavaScript 會(huì)做什么告组,所以它采取的預(yù)防措施是停止整個(gè) DOM 構(gòu)造。

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>

    <script>
        let header = document.getElementById("header");

        console.log("header is: ", header);
    </script>
</body>

</html>

在腳本標(biāo)簽中癌佩,我將訪問(wèn) id 為 header 的 DOM 節(jié)點(diǎn)木缝,然后將其輸出到控制臺(tái)∥д蓿可以正常運(yùn)行我碟。

但是,你是否注意到姚建,這個(gè)腳本標(biāo)簽位于 body 標(biāo)簽的底部矫俺?讓我們把它放在 head 中,看看會(huì)發(fā)生什么:一旦我這樣做掸冤,header 就解析為 null厘托。

為什么會(huì)這樣?很簡(jiǎn)單稿湿。當(dāng) HTML 解析器正在構(gòu)建 DOM 時(shí)铅匹,發(fā)現(xiàn)了一個(gè)腳本標(biāo)簽。此時(shí)饺藤,body 標(biāo)簽及其所有內(nèi)容還沒(méi)有被解析伊群。DOM 構(gòu)造將停止,直到腳本執(zhí)行完成:

當(dāng)腳本試圖訪問(wèn)一個(gè) id 為 header 的 DOM 節(jié)點(diǎn)時(shí)策精,由于 DOM 還沒(méi)有完成對(duì)文檔的解析,所以它還不存在崇棠。這把我們帶到了另一個(gè)重要的問(wèn)題咽袜。腳本的位置很重要。

這還不是全部枕稀。如果你將內(nèi)聯(lián)腳本提取到外部本地文件 app.js 中询刹,行為是一樣的谜嫉。DOM 的構(gòu)建仍然會(huì)停止:

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
    <script src="app.js"></script>
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>

那么,如果 app.js 不是本地的而必須通過(guò)互聯(lián)網(wǎng)獲取呢凹联?如果網(wǎng)速很慢沐兰,需要數(shù)千毫秒來(lái)獲取 app.js,DOM 的構(gòu)建也會(huì)暫停幾千毫秒1文印W〈场!這是一個(gè)很大的性能問(wèn)題澳淑,而且還不止于此比原。JavaScript 還可以訪問(wèn) CSSOM 并對(duì)其進(jìn)行修改。例如杠巡,這是有效的 JavaScript 語(yǔ)句:

document.getElementsByTagName("body")[0].style.backgroundColor = "red";

那么量窘,當(dāng)解析器遇到一個(gè)腳本標(biāo)簽而 CSSOM 還沒(méi)有準(zhǔn)備好時(shí),會(huì)發(fā)生什么情況呢氢拥?答案很簡(jiǎn)單蚌铜。Javascript 執(zhí)行將會(huì)停止,直到 CSSOM 就緒嫩海。因此冬殃,雖然 DOM 構(gòu)造在遇到腳本標(biāo)簽時(shí)會(huì)停止,但 CSSOM 不會(huì)發(fā)生這種情況出革。對(duì)于 CSSOM造壮,JS 執(zhí)行會(huì)等待。沒(méi)有 CSSOM骂束,就沒(méi)有 JS 執(zhí)行耳璧。

async 屬性

在默認(rèn)情況下,每個(gè)腳本都是一個(gè)解析器阻斷器展箱!DOM 的構(gòu)建總是會(huì)被打斷旨枯。不過(guò),有一種方法可以改變這種默認(rèn)行為混驰。如果將 async 關(guān)鍵字添加到腳本標(biāo)簽中攀隔,那么 DOM 構(gòu)造就不會(huì)停止。DOM 構(gòu)造將繼續(xù)栖榨,腳本將在下載完成并準(zhǔn)備就緒后執(zhí)行昆汹。

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Medium Article Demo</title>
    <link rel="stylesheet" href="style.css">
    <script src="https://some-link-to-app.js" async></script>
</head>

<body>

    <p id="header">How Browser Rendering Works</p>
    <div><img src="https://i.imgur.com/jDq3k3r.jpg"></div>
</body>

</html>

關(guān)鍵渲染路徑

目前為止,我們討論了從接收 HTML婴栽、CSS 和 JS 字節(jié)到將它們轉(zhuǎn)換為屏幕上的像素之間的所有步驟满粗。這整個(gè)過(guò)程稱為關(guān)鍵渲染路徑。優(yōu)化網(wǎng)站性能就是優(yōu)化關(guān)鍵渲染路徑愚争。

一個(gè)經(jīng)過(guò)良好優(yōu)化的站點(diǎn)應(yīng)該能夠漸進(jìn)式渲染映皆,而不是讓整個(gè)過(guò)程受阻挤聘。

這是 Web 應(yīng)用程序慢或快的區(qū)別所在。周密的關(guān)鍵渲染路徑(CRP)優(yōu)化策略使瀏覽器能夠通過(guò)確定優(yōu)先加載的資源以及資源加載的順序來(lái)盡可能快地加載頁(yè)面捅彻。

常見(jiàn)引起回流屬性和方法

任何會(huì)改變?cè)貛缀涡畔?元素的位置和尺寸大小)的操作组去,都會(huì)觸發(fā)回流,

  • 添加或者刪除可見(jiàn)的DOM元素步淹;
  • 元素尺寸改變——邊距从隆、填充、邊框贤旷、寬度和高度
  • 內(nèi)容變化广料,比如用戶在input框中輸入文字
  • 瀏覽器窗口尺寸改變——resize事件發(fā)生時(shí)
  • 計(jì)算 offsetWidth 和 offsetHeight 屬性
  • 設(shè)置 style 屬性的值

常見(jiàn)引起重繪屬性和方法

下面例子中,觸發(fā)了幾次回流和重繪幼驶?

var s = document.body.style;
s.padding = "2px"; // 回流+重繪
s.border = "1px solid red"; // 再一次 回流+重繪
s.color = "blue"; // 再一次重繪
s.backgroundColor = "#ccc"; // 再一次 重繪
s.fontSize = "14px"; // 再一次 回流+重繪
// 添加node艾杏,再一次 回流+重繪
document.body.appendChild(document.createTextNode('abc!'));

如何減少回流、重繪

  • 使用 transform 替代 top
  • 使用 visibility 替換 display: none 盅藻,因?yàn)榍罢咧粫?huì)引起重繪购桑,后者會(huì)引發(fā)回流(改變了布局)
  • 不要把節(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量。
  • 不要使用 table 布局氏淑,可能很小的一個(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局
  • 動(dòng)畫實(shí)現(xiàn)的速度的選擇勃蜘,動(dòng)畫速度越快,回流次數(shù)越多假残,也可以選擇使用 requestAnimationFrame
  • CSS 選擇符從右往左匹配查找缭贡,避免節(jié)點(diǎn)層級(jí)過(guò)多
  • 將頻繁重繪或者回流的節(jié)點(diǎn)設(shè)置為圖層,圖層能夠阻止該節(jié)點(diǎn)的渲染行為影響別的節(jié)點(diǎn)辉懒。比如對(duì)于 video 標(biāo)簽來(lái)說(shuō)阳惹,瀏覽器會(huì)自動(dòng)將該節(jié)點(diǎn)變?yōu)閳D層。

async和defer的作用是什么眶俩?有什么區(qū)別?

接下來(lái)我們對(duì)比下 defer 和 async 屬性的區(qū)別:

(1)情況1<script src="script.js"></script>

沒(méi)有 defer 或 async莹汤,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本,也就是說(shuō)不等待后續(xù)載入的文檔元素颠印,讀到就加載并執(zhí)行纲岭。

(2)情況2<script async src="script.js"></script> (異步下載)

async 屬性表示異步執(zhí)行引入的 JavaScript,與 defer 的區(qū)別在于线罕,如果已經(jīng)加載好止潮,就會(huì)開(kāi)始執(zhí)行——無(wú)論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發(fā)之后。需要注意的是钞楼,這種方式加載的 JavaScript 依然會(huì)阻塞 load 事件沽翔。換句話說(shuō),async-script 可能在 DOMContentLoaded 觸發(fā)之前或之后執(zhí)行,但一定在 load 觸發(fā)之前執(zhí)行仅偎。

(3)情況3 <script defer src="script.js"></script>(延遲執(zhí)行)

defer 屬性表示延遲執(zhí)行引入的 JavaScript,即這段 JavaScript 加載時(shí) HTML 并未停止解析雳殊,這兩個(gè)過(guò)程是并行的橘沥。整個(gè) document 解析完畢且 defer-script 也加載完成之后(這兩件事情的順序無(wú)關(guān)),會(huì)執(zhí)行所有由 defer-script 加載的 JavaScript 代碼夯秃,然后觸發(fā) DOMContentLoaded 事件座咆。

defer 與相比普通 script,有兩點(diǎn)區(qū)別:載入 JavaScript 文件時(shí)不阻塞 HTML 的解析仓洼,執(zhí)行階段被放到 HTML 標(biāo)簽解析完成之后介陶。在加載多個(gè)JS腳本的時(shí)候,async是無(wú)順序的加載色建,而defer是有順序的加載哺呜。

為什么操作 DOM 慢

因?yàn)?DOM 是屬于渲染引擎中的東西,而 JS 又是 JS 引擎中的東西箕戳。當(dāng)我們通過(guò) JS 操作 DOM 的時(shí)候某残,其實(shí)這個(gè)操作涉及到了兩個(gè)線程之間的通信,那么勢(shì)必會(huì)帶來(lái)一些性能上的損耗陵吸。操作 DOM 次數(shù)一多玻墅,也就等同于一直在進(jìn)行線程之間的通信,并且操作 DOM 可能還會(huì)帶來(lái)重繪回流的情況壮虫,所以也就導(dǎo)致了性能上的問(wèn)題澳厢。

渲染頁(yè)面時(shí)常見(jiàn)哪些不良現(xiàn)象?

由于瀏覽器的渲染機(jī)制不同囚似,在渲染頁(yè)面時(shí)會(huì)出現(xiàn)兩種常見(jiàn)的不良現(xiàn)象----白屏問(wèn)題和FOUS(無(wú)樣式內(nèi)容閃爍)

FOUC:由于瀏覽器渲染機(jī)制(比如firefox)剩拢,再CSS加載之前,先呈現(xiàn)了HTML谆构,就會(huì)導(dǎo)致展示出無(wú)樣式內(nèi)容裸扶,然后樣式突然呈現(xiàn)的現(xiàn)象;

白屏:有些瀏覽器渲染機(jī)制(比如chrome)要先構(gòu)建DOM樹(shù)和CSSOM樹(shù)搬素,構(gòu)建完成后再進(jìn)行渲染呵晨,如果CSS部分放在HTML尾部,由于CSS未加載完成熬尺,瀏覽器遲遲未渲染摸屠,從而導(dǎo)致白屏;也可能是把js文件放在頭部粱哼,腳本會(huì)阻塞后面內(nèi)容的呈現(xiàn)季二,腳本會(huì)阻塞其后組件的下載,出現(xiàn)白屏問(wèn)題。

總結(jié)

  1. 瀏覽器工作流程:構(gòu)建DOM -> 構(gòu)建CSSOM -> 構(gòu)建渲染樹(shù) -> 布局 -> 繪制胯舷。
  2. 當(dāng)瀏覽器接收到原始數(shù)據(jù)字節(jié)并啟動(dòng) DOM 構(gòu)建過(guò)程時(shí)刻蚯,它還會(huì)發(fā)出請(qǐng)求來(lái)獲取鏈接的 main.css 樣式表,啟動(dòng)CSSOM構(gòu)建
  3. 構(gòu)建DOM的過(guò)程中桑嘶,不是等所有Token都轉(zhuǎn)換完成后再去生成節(jié)點(diǎn)對(duì)象炊汹,而是一邊生成Token一邊消耗Token來(lái)生成節(jié)點(diǎn)對(duì)象。換句話說(shuō)逃顶,每個(gè)Token被生成后讨便,會(huì)立刻消耗這個(gè)Token創(chuàng)建出節(jié)點(diǎn)對(duì)象
  4. DOM 和 CSSOM 樹(shù)結(jié)構(gòu)是兩個(gè)獨(dú)立的樹(shù)結(jié)構(gòu)。DOM 中包含關(guān)于頁(yè)面 HTML 元素關(guān)系的所有信息以政,而 CSSOM 則包含關(guān)于元素樣式的信息霸褒。
  5. 瀏覽器得遞歸 CSSOM 樹(shù),然后確定具體的元素到底是什么樣式盈蛮,注意:CSS匹配HTML元素是一個(gè)相當(dāng)復(fù)雜和有性能問(wèn)題的事情废菱。所以,DOM樹(shù)要小眉反,CSS盡量用id和class昙啄,千萬(wàn)不要過(guò)渡層疊下去
  6. CSSOM會(huì)阻塞渲染,只有當(dāng)CSSOM構(gòu)建完畢后才會(huì)進(jìn)入下一個(gè)階段構(gòu)建渲染樹(shù)寸五。
  7. 渲染樹(shù)包含頁(yè)面上所有關(guān)于可見(jiàn) DOM 內(nèi)容的信息以及不同節(jié)點(diǎn)所需的所有 CSSOM 信息梳凛,隱藏元素會(huì)出現(xiàn)在 DOM 中,但不會(huì)出現(xiàn)在渲染樹(shù)中梳杏。這是因?yàn)殇秩緲?shù)結(jié)合了來(lái)自 DOM 和 CSSOM 的信息韧拒,所以它知道不能把隱藏元素包含在樹(shù)中。
  8. 構(gòu)建好渲染樹(shù)之后十性,瀏覽器必須計(jì)算頁(yè)面上每個(gè)對(duì)象的確切大小和位置叛溢,這個(gè)布局步驟考慮了從 DOM 和 CSSOM 接收到的內(nèi)容和樣式,并執(zhí)行了所有必要的布局計(jì)算(回流或者自動(dòng)重排)劲适。
  9. 每當(dāng)瀏覽器遇到腳本標(biāo)簽時(shí)楷掉,DOM 構(gòu)造就會(huì)暫停!整個(gè) DOM 構(gòu)建過(guò)程都將停止霞势,但 CSSOM 不會(huì)發(fā)生這種情況烹植,直到腳本執(zhí)行完成,當(dāng)解析器遇到一個(gè)腳本標(biāo)簽而 CSSOM 還沒(méi)有準(zhǔn)備好時(shí)愕贡,Javascript 執(zhí)行將會(huì)停止草雕,直到 CSSOM 就緒,對(duì)于 CSSOM固以,JS 執(zhí)行會(huì)等待墩虹。
  10. 通常情況下DOM和CSSOM是并行構(gòu)建的嘱巾,但是當(dāng)瀏覽器遇到一個(gè)script標(biāo)簽時(shí)筒愚,DOM構(gòu)建將暫停炕吸,直至腳本完成執(zhí)行。但由于JavaScript可以修改CSSOM览闰,所以需要等CSSOM構(gòu)建完畢后再執(zhí)行JS尖坤。
  11. JS文件不只是阻塞DOM的構(gòu)建稳懒,它會(huì)導(dǎo)致CSSOM也阻塞DOM的構(gòu)建。原本DOM和CSSOM的構(gòu)建是互不影響慢味,井水不犯河水,但是一旦引入了JavaScript墅冷,CSSOM也開(kāi)始阻塞DOM的構(gòu)建纯路,只有CSSOM構(gòu)建完畢后,DOM再恢復(fù)DOM構(gòu)建寞忿。在這種情況下驰唬,瀏覽器會(huì)先下載和構(gòu)建CSSOM,然后再執(zhí)行JavaScript腔彰,最后在繼續(xù)構(gòu)建DOM叫编。
  12. 如果你想首屏渲染的越快,就越不應(yīng)該在首屏就加載 JS 文件霹抛,建議將 script 標(biāo)簽放在 body 標(biāo)簽底部搓逾。
  13. 重繪:當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀杯拐、風(fēng)格霞篡,而不會(huì)影響布局的,比如background-color端逼。
  14. 回流:當(dāng)render tree中的一部分(或全部)因?yàn)樵氐囊?guī)模尺寸朗兵、布局、隱藏等改變而需要重新構(gòu)建
  15. 回流必定會(huì)發(fā)生重繪顶滩,重繪不一定會(huì)引發(fā)回流余掖。重繪和回流會(huì)在我們?cè)O(shè)置節(jié)點(diǎn)樣式時(shí)頻繁出現(xiàn),同時(shí)也會(huì)很大程度上影響性能礁鲁⊙纹郏回流所需的成本比重繪高的多,改變父節(jié)點(diǎn)里的子節(jié)點(diǎn)很可能會(huì)導(dǎo)致父節(jié)點(diǎn)的一系列回流救氯。

參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末着憨,一起剝皮案震驚了整個(gè)濱河市墩衙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖漆改,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件心铃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡挫剑,警方通過(guò)查閱死者的電腦和手機(jī)去扣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)樊破,“玉大人愉棱,你說(shuō)我怎么就攤上這事≌芷荩” “怎么了奔滑?”我有些...
    開(kāi)封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)顺少。 經(jīng)常有香客問(wèn)我朋其,道長(zhǎng),這世上最難降的妖魔是什么脆炎? 我笑而不...
    開(kāi)封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任梅猿,我火速辦了婚禮,結(jié)果婚禮上秒裕,老公的妹妹穿的比我還像新娘袱蚓。我一直安慰自己,他們只是感情好簇爆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布癞松。 她就那樣靜靜地躺著,像睡著了一般入蛆。 火紅的嫁衣襯著肌膚如雪响蓉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天哨毁,我揣著相機(jī)與錄音枫甲,去河邊找鬼。 笑死扼褪,一個(gè)胖子當(dāng)著我的面吹牛想幻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播话浇,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼脏毯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了幔崖?” 一聲冷哼從身側(cè)響起食店,我...
    開(kāi)封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤渣淤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吉嫩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體价认,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年自娩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了用踩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忙迁,死狀恐怖脐彩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情姊扔,我是刑警寧澤丁屎,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旱眯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏证九。R本人自食惡果不足惜删豺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愧怜。 院中可真熱鬧呀页,春花似錦、人聲如沸拥坛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猜惋。三九已至丸氛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間著摔,已是汗流浹背缓窜。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谍咆,地道東北人禾锤。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像摹察,于是被迫代替她去往敵國(guó)和親恩掷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 大家都知道萬(wàn)維網(wǎng)的應(yīng)用層使用了HTTP協(xié)議供嚎,并且用瀏覽器作為入口訪問(wèn)網(wǎng)絡(luò)上的資源黄娘。用戶在使用瀏覽器訪問(wèn)一個(gè)網(wǎng)站時(shí)需...
    SylvanasSun閱讀 2,151評(píng)論 1 12
  • 瀏覽器指的是Chrome系瀏覽器【Firefox大同小異峭状,IE未知】以下提到的“節(jié)點(diǎn)”、“標(biāo)簽”和“元素”不做區(qū)分...
    Yieiy閱讀 4,106評(píng)論 4 26
  • 關(guān)鍵渲染路徑 瀏覽器從接收到頁(yè)面開(kāi)始到頁(yè)面顯示寸宏,這整個(gè)過(guò)程中的所有步驟宁炫,稱為關(guān)鍵渲染路徑。用戶看到頁(yè)面實(shí)際上可以分...
    ZombieBrandg閱讀 500評(píng)論 0 0
  • 親愛(ài)的 楓葉又紅了 紅葉也瘋了 正午 陽(yáng)光好暖 放下吧 就曬太陽(yáng) 就目空一切 就無(wú)所事事 就一事無(wú)成 就這樣 呆在陽(yáng)光里
    一片_楓葉閱讀 249評(píng)論 0 0
  • 有個(gè)小男孩,暑假被麻麻糖衣加炮彈的哄騙去了新東方上幼小銜接課程罩阵,幼小銜接這個(gè)名詞我是在五年前知道的竿秆,當(dāng)時(shí)娃尚在襁褓...
    陳娟米奇閱讀 579評(píng)論 1 6