hello~各位親愛的看官老爺們你們好。估計你們都聽過,儘可能將CSS
放頭部,JS
放底部,這樣能夠提升頁面的性能。然而,爲何呢?你們有考慮過麼?很長一段時間,我都是知其然而不知其因此然,強行背下來應付考覈固然能夠,但實際應用中必然一塌糊塗。所以洗(wang)心(yang)革(bu)面(lao),小結一下最近玩出來的成果。css
友情提示,本文也是小白向爲主,若是直接想看結論能夠拉到最下面看的~html
因爲關係到文件的讀取,那是確定須要服務器的,我會把所有的文件放在github上,給我點個 star 我會開心!掘金上再給我點個 贊 我就更開心了~node
node
端惟一須要解釋一下的是這個函數:git
function sleep(time) {
return new Promise(function(res) {
setTimeout(() => {
res()
}, time);
})
}
複製代碼
嗯!其實就延時啦。若是CSS
或者JS
文件名有sleep3000
之類的前綴時,意思就是延遲3000毫秒纔會返回這文件。github
下文使用的HTML
文件是長這樣的:數組
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
width: 100px;
height: 100px;
background: lightgreen;
}
</style>
</head>
<body>
<div></div>
</body>
</html>
複製代碼
我會在其中插入不一樣的JS
和CSS
。瀏覽器
而使用的common.css
,不論有沒有前綴,內容都是這樣的:bash
div {
background: lightblue;
}
複製代碼
好了,話很少數,開始正文!服務器
關於CSS
,你們確定都知道的是<link>
標籤放在頭部性能會高一點,少一點人知道若是<script>
與<link>
同時在頭部的話,<script>
在上可能會更好。這是爲何呢?下面咱們一塊兒來看一下CSS
對DOM
的影響是什麼。dom
CSS
不會阻塞 DOM
的解析注意哦!這裏說的是DOM
解析,證實的例子以下,首先在頭部插入<script defer src="/js/logDiv.js"></script>
,JS
文件的內容是:
const div = document.querySelector('div');
console.log(div);
複製代碼
defer
屬性相信你們也很熟悉了,MDN對此的描述是用來通知瀏覽器該腳本將在文檔完成解析後,觸發 DOMContentLoaded 事件前執行。設置這個屬性,能保證DOM
解析後立刻打印出div
。
以後將<link rel="stylesheet" href="/css/sleep3000-common.css">
插入HTML
文件的任一位置,打開瀏覽器,能夠看到是首先打印出div
這個DOM
節點,過3s左右以後才渲染出一個淺藍色的div
。這就證實了CSS
是不會阻塞 DOM
的解析的,儘管CSS
下載須要3s,但這個過程當中,瀏覽器不會傻等着CSS
下載完,而是會解析DOM
的。
這裏簡單說一下,瀏覽器是解析DOM
生成DOM Tree
,結合CSS
生成的CSS Tree
,最終組成render tree
,再渲染頁面。因而可知,在此過程當中CSS
徹底沒法影響DOM Tree
,於是無需阻塞DOM
解析。然而,DOM Tree
和CSS Tree
會組合成render tree
,那CSS
會不會頁面阻塞渲染呢?
CSS
阻塞頁面渲染其實這一點,剛纔的例子已經說明了,若是CSS
不會阻塞頁面阻塞渲染,那麼CSS
文件下載以前,瀏覽器就會渲染出一個淺綠色的div
,以後再變成淺藍色。瀏覽器的這個策略其實很明智的,想象一下,若是沒有這個策略,頁面首先會呈現出一個原始的模樣,待CSS
下載完以後又忽然變了一個模樣。用戶體驗可謂極差,並且渲染是有成本的。
所以,基於性能與用戶體驗的考慮,瀏覽器會盡可能減小渲染的次數,CSS
瓜熟蒂落地阻塞頁面渲染。
然而,事情總有奇怪的,請看這例子,HTML
頭部結構以下:
<header>
<link rel="stylesheet" href="/css/sleep3000-common.css">
<script src="/js/logDiv.js"></script>
</header>
複製代碼
但思考一下這會產生什麼結果呢?
答案是瀏覽器會轉圈圈三秒,但此過程當中不會打印任何東西,以後呈現出一個淺藍色的div
,再打印出null
。結果好像是CSS
不單阻塞了頁面渲染,還阻塞了DOM
的解析啊!稍等,在你打算掀桌子瘋狂吐槽我以前,請先思考一下是什麼阻塞了DOM
的解析,剛纔已經證實了CSS
是不會阻塞的,那麼阻塞了頁面解析實際上是JS
!但明明JS
的代碼如此簡單,確定不會阻塞這麼久,那就是JS
在等待CSS
的下載,這是爲何呢?
仔細思考一下,其實這樣作是有道理的,若是腳本的內容是獲取元素的樣式,寬高等CSS
控制的屬性,瀏覽器是須要計算的,也就是依賴於CSS
。瀏覽器也沒法感知腳本內容究竟是什麼,爲避免樣式獲取,於是只好等前面全部的樣式下載完後,再執行JS
。於是形成了以前例子的狀況。
因此,看官大人明白爲什麼<script>
與<link>
同時在頭部的話,<script>
在上可能會更好了麼?之因此是可能,是由於若是<link>
的內容下載更快的話,是沒影響的,但反過來的話,JS
就要等待了,然而這些等待的時間是徹底沒必要要的。
JS
,也就是<script>
標籤,估計你們都很熟悉了,不就是阻塞DOM
解析和渲染麼。然而,其中其實仍是有一點細節能夠考究一下的,咱們一塊兒來好好看看。
JS
阻塞 DOM
解析首先咱們須要一個新的JS
文件名爲blok.js
,內容以下:
const arr = [];
for (let i = 0; i < 10000000; i++) {
arr.push(i);
arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector('div');
console.log(div);
複製代碼
其實那個數組操做時沒意義的,只是爲了讓這個JS
文件多花執行時間而已。以後把這個文件插入頭部,瀏覽器跑一下。
結果估計你們也能想象獲得,瀏覽器轉圈圈一會,這過程當中不會有任何東西出現。以後打印出null
,再出現一個淺綠色的div
。現象就足以說明JS
阻塞 DOM
解析了。其實緣由也很好理解,瀏覽器並不知道腳本的內容是什麼,若是先行解析下面的DOM
,萬一腳本內全刪了後面的DOM
,瀏覽器就白乾活了。更別談喪心病狂的document.write
。瀏覽器沒法預估裏面的內容,那就乾脆所有停住,等腳本執行完再幹活就行了。
對此的優化其實也很顯而易見,具體分爲兩類。若是JS
文件體積太大,同時你肯定不必阻塞DOM
解析的話,不妨按須要加上defer
或者async
屬性,此時腳本下載的過程當中是不會阻塞DOM
解析的。
而若是是文件執行時間太長,不妨分拆一下代碼,不用當即執行的代碼,可使用一下之前的黑科技:setTimeout()
。固然,現代的瀏覽器很聰明,它會「偷看」以後的DOM
內容,碰到如<link>
、<script>
和<img>
等標籤時,它會幫助咱們先行下載裏面的資源,不會傻等到解析到那裏時才下載。
<script>
標籤時,會觸發頁面渲染這個細節可能很多看官大人並不清楚,其實這纔是解釋上面爲什麼JS
執行會等待CSS
下載的緣由。先上例子,HTML
內body
的結構以下:
<body>
<div></div>
<script src="/js/sleep3000-logDiv.js"></script>
<style>
div {
background: lightgrey;
}
</style>
<script src="/js/sleep5000-logDiv.js"></script>
<link rel="stylesheet" href="/css/common.css">
</body>
複製代碼
這個例子也是很極端的例子,但不妨礙它透露給咱們不少重要的信息。想象一下,頁面會怎樣呢?
答案是先淺綠色,再淺灰色,最後淺藍色。因而可知,每次碰到<script>
標籤時,瀏覽器都會渲染一次頁面。這是基於一樣的理由,瀏覽器不知道腳本的內容,於是碰到腳本時,只好先渲染頁面,確保腳本能獲取到最新的DOM
元素信息,儘管腳本可能不須要這些信息。
綜上所述,咱們得出這樣的結論:
CSS
不會阻塞 DOM
的解析,但會阻塞 DOM
渲染。JS
阻塞 DOM
解析,但瀏覽器會"偷看"DOM
,預先下載相關資源。<script>
且沒有defer
或async
屬性的 標籤時,會觸發頁面渲染,於是若是前面CSS
資源還沒有加載完畢時,瀏覽器會等待它加載完畢在執行腳本。因此,你如今明白爲什麼<script>
最好放底部,<link>
最好放頭部,若是頭部同時有<script>
與<link>
的狀況下,最好將<script>
放在<link>
上面了嗎?
感謝各位看官大人看到這裏,但願本文對你有所幫助,有不一樣或更好意見的大佬,還望不吝賜教!謝謝~