首頁 > web前端 > css教學 > 主體

聊聊CSS 與 JS 是如何阻塞 DOM 解析與渲染的

青灯夜游
發布: 2021-05-27 09:58:16
轉載
2728 人瀏覽過

本篇文章為大家介紹CSS和JS阻塞 DOM 解析和渲染的原理。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

聊聊CSS 與 JS 是如何阻塞 DOM 解析與渲染的

hello~各位親愛的看官老爺們大家好。估計大家都聽過,盡量將CSS放頭部,JS放底部,這樣可以提升頁面的效能。然而,為什麼呢?大家有考慮過麼?很長一段時間,我都是知其然而不知其所以然,強行背下來應付考核當然可以,但實際應用中必然一塌糊塗。因此洗(wang)心(yang)革(bu)面(lao),小結一下最近玩出來的成果。

友情提示,本文也是小白向為主,如果直接想看結論可以拉到最下面看的~


由於關係到文件的讀取,那是肯定需要伺服器的,我會把全部的檔案放在github上,給我點個star 我會開心!

node端唯一需要解釋一下的是這個函數:

function sleep(time) {
  return new Promise(function(res) {
    setTimeout(() => {
      res()
    }, time);
  })
}
登入後複製

嗯!其實就延時啦。如果CSSJS檔案名稱有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>
登入後複製

我會在其中插入不同的JSCSS

而使用的common.css,不論有沒有前綴,內容都是這樣的:

div {
  background: lightblue;
}
登入後複製

好了,話不多數,開始正文!

CSS

關於CSS,大家一定都知道的是<link>標籤放在頭部性能會高一點,少一點人知道如果<script><link>同時在頭部的話,<script>在上可能會更好。這是為什麼呢?下面我們一起來看看CSSDOM的影響是什麼。

CSS 不會阻塞 DOM 的解析

注意哦!這裡說的是DOM 解析,證明的例子如下,先在頭部插入<script defer src="/js/logDiv.js"></script>JS檔案的內容是:

const div = document.querySelector(&#39;div&#39;);
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 TreeCSS 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

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(&#39;div&#39;);
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下载的原因。先上例子,HTMLbody的结构如下:

<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>且沒有deferasync屬性的標籤時,會觸發頁面渲染,因而如果前面CSS資源尚未載入完畢時,瀏覽器會等待它載入完畢在執行腳本。

所以,你現在明白為何<script>最好放底部,<link>最好放頭部,如果頭部同時有<script><link>的情況下,最好將<script>放在<link>上面了嗎?

感謝各位看官大人看到這裡,希望本文對你有幫助,有不同或更好意見的大佬,還望不吝賜教!謝謝~

原文網址:https://juejin.cn/post/6844903497599549453

更多程式相關知識,請造訪:程式設計影片! !

以上是聊聊CSS 與 JS 是如何阻塞 DOM 解析與渲染的的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板