本篇文章為大家介紹CSS和JS阻塞 DOM 解析和渲染的原理。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
hello~各位親愛的看官老爺們大家好。估計大家都聽過,盡量將CSS
放頭部,JS
放底部,這樣可以提升頁面的效能。然而,為什麼呢?大家有考慮過麼?很長一段時間,我都是知其然而不知其所以然,強行背下來應付考核當然可以,但實際應用中必然一塌糊塗。因此洗(wang)心(yang)革(bu)面(lao),小結一下最近玩出來的成果。
友情提示,本文也是小白向為主,如果直接想看結論可以拉到最下面看的~
由於關係到文件的讀取,那是肯定需要伺服器的,我會把全部的檔案放在github上,給我點個star 我會開心!
node
端唯一需要解釋一下的是這個函數:
function sleep(time) { return new Promise(function(res) { setTimeout(() => { res() }, time); }) }
嗯!其實就延時啦。如果CSS
或JS
檔案名稱有sleep3000
之類的前綴時,意思就是延遲3000毫秒才會回傳這檔。
下文使用的HTML
檔案是長這樣的:
<!DOCTYPE html> <html> <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
,不論有沒有前綴,內容都是這樣的:
div { background: lightblue; }
好了,話不多數,開始正文!
關於CSS
,大家一定都知道的是<link>
標籤放在頭部性能會高一點,少一點人知道如果<script>
與<link>
同時在頭部的話,<script>
在上可能會更好。這是為什麼呢?下面我們一起來看看CSS
對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>
上面了嗎?
感謝各位看官大人看到這裡,希望本文對你有幫助,有不同或更好意見的大佬,還望不吝賜教!謝謝~
原文網址:https://juejin.cn/post/6844903497599549453
更多程式相關知識,請造訪:程式設計影片! !
以上是聊聊CSS 與 JS 是如何阻塞 DOM 解析與渲染的的詳細內容。更多資訊請關注PHP中文網其他相關文章!