<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
一個頁面允許載入的外部資源有很多,常見的有指令碼、樣式、字型、圖片和視訊等,對於這些外部資源究竟是如何影響整個頁面的載入和渲染的呢?今天來一探究竟。
測試之前需要對瀏覽器下載資源的速度進行控制,將它重新設定為 50kb/s,操作方式:
為什麼是這個速度?因為如下的一些資源,比如圖片、樣式或者指令碼體積都是 50kb 的好幾倍,方便測試。
直接寫個範例來看下結果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <script> document.addEventListener('DOMContentLoaded', () => { console.log('DOMContentLoaded') }) window.onload = function() { console.log('onload') } </script> </head> <body> <h1>我是 h1 標籤</h1> <img src="https://xxx.oss-cn-shenzhen.aliyuncs.com/images/flow.png" /> <h2>我是 h2 標籤</h2> </body> </html>
上面這張圖片的大小大概是 200kb,當把網路下載速度限制成 50kb/s,開啟該頁面,可以看到如下結果:當 h1
和 h2
標籤渲染出來且列印了 DOMContentLoaded
的時候,此時圖片還在載入中,這就說明了圖片並不會阻塞 DOM
的載入,更加不會阻塞頁面渲染;當圖片載入完成的時候,會列印 onload
,說明圖片延遲了 onload
事件的觸發。
視訊、字型和圖片其實是一樣的,也不會阻塞 DOM
的載入和渲染。
同樣的,還是直接用程式碼來測試 CSS 載入對頁面阻塞的情況,因為下面程式碼載入的 bootstrap.css
是 192kb 的,所以理論上下載它應該需要花費 3 到 4 秒左右。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet" /> </head> <body> <h1>我是 h1 標籤</h1> </body> </html>
測試過程如下:
Elements
面板下,選中 h1
這個標籤,然後按 delete
鍵將它從 DOM
中刪掉,從而模擬首次載入;Elements
面板下就載入出 h1
標籤,繼續載入 3 到 4 秒後(此時正在載入 bootstrap.css),頁面出現 我是 h1 標籤
字樣,此時頁面已經渲染完成。從而得出結論:
DOM
中就已經出現 h1 標籤,說明 CSS 不會阻塞 DOM 的解析;為什麼是這個結論呢?試想一下頁面渲染的流程就知道了。瀏覽器首先解析 HTML 生成 DOM 樹,解析 CSS
生成 CSSOM
樹,然後 DOM
樹和 CSSOM
樹進行合成生成渲染樹,通過渲染樹進行佈局並且計算每個節點資訊,繪製頁面。
可以說解析 DOM 和 解析 CSS 其實是並列進行的,既然是並列進行的,那 CSS 和 DOM 就不會互相影響了,這和結論一相符;另外渲染頁面一定是在得到 CSSOM 樹之後進行的,這和結論二相符。
CSS 一定會阻塞 DOM 的渲染嘛?答案是否定的,當把外連樣式放到 <body>
最尾部去載入:
<body> <h1>我是 h1 標籤</h1> <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet" /> </body>
此時重新整理瀏覽器,頁面上會馬上顯示出 我是 h1 標籤
字樣,當 3 到 4 秒過後樣式載入完成的時會造成二次渲染,頁面重新渲染出該字樣,這就說明 CSS 阻塞 DOM 的渲染只阻塞定義在 CSS 後面的 DOM。二次渲染會對使用者造成不好的體驗且加重了瀏覽器的負擔,所以這也就是為什麼需要把外連樣式提前到 <head>
裡載入的原因。
CSS 阻塞了後面 DOM 的渲染,那它會阻塞 JS 的執行嘛?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet" /> </head> <body> <h1>我是 h1 標籤</h1> <script> console.log('888') </script> </body> </html>
重新整理瀏覽器的時候可以看到,瀏覽器 Console
面板下沒有列印內容,而當樣式載入完成的時候列印了 888,這就說明 CSS 會阻塞定義在其之後 JS 的執行。
為什麼會這樣呢?試想一下,如果 JS 裡執行的操作需要獲取當前 h1 標籤的樣式,而由於樣式沒載入完成,所以就無法得到想要的結果,從而證明了 CSS 需要阻塞定義在其之後 JS 的執行。
CSS 會阻塞 DOM 的渲染和阻塞定義在其之後的 JS 的執行,那 JS 載入會對渲染過程造成什麼影響呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> </head> <body> <h1>我是 h1 標籤</h1> </body> </html>
首先刪除頁面中已經存在的 h1 標籤(如果存在的話),仔細觀察 Elements 面板,當重新整理瀏覽器的時候,一直未載入出 h1 標籤(期間頁面一直白屏),直到 JS 載入完成後,DOM 中才出現,這足以說明了 JS 會阻塞定義在其之後的 DOM
的載入,所以應該將外部 JS 放到 <body>
的最尾部去載入,減少頁面載入白屏時間。
JS 一定會阻塞定義在其之後的 DOM 的載入嗎?來測試一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <script async src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> </head> <body> <h1>我是 h1 標籤</h1> </body> </html>
上面這段程式碼的測試結果是當頁面中顯示出 h1 標籤的時候,指令碼還沒有載入完成,這就說明了 async
指令碼不會阻塞 DOM 的載入;同理可以用同樣的方式測試 defer
,也會得到這個結論。
現在知道了通過 defer
或者 async
方式載入 JS 的時候,它是不會阻塞 DOM 載入的。知道 defer
和 async
是什麼嗎?它們兩者有什麼區別呢?
回答這些疑問之前,先來看下當瀏覽器解析 HTML 遇到 script
標籤的時候會發生什麼?
script
裡的指令碼,如果該 script
是外連,則會先下載它,下載完成後立刻執行;上面這是解析時遇到一個正常的外連的情況,正常外連的下載和執行都會阻塞頁面解析;而如果外連是通過 defer
或者 async
載入的時候又會是如何呢?
defer
特點
defer
的 script
,瀏覽器會繼續解析 html
,且同時並行下載指令碼,等 DOM
構建完成後,才會開始執行指令碼,所以它不會造成阻塞;defer
指令碼下載完成後,執行時間一定是 DOMContentLoaded
事件觸發之前執行;defer
的指令碼執行順序嚴格按照定義順序進行,而不是先下載好的先執行;async
特點
async
的 script
,瀏覽器會繼續解析 html
,且同時並行下載指令碼,一旦指令碼下載完成會立刻執行;和 defer 一樣,它在下載的時候也不會造成阻塞,但是如果它下載完成後 DOM
還沒解析完成,則執行指令碼的時候是會阻塞解析的;async
指令碼的執行 和 DOMContentLoaded
的觸發順序無法明確誰先誰後,因為指令碼可能在 DOM
構建完成時還沒下載完,也可能早就下載好了;async
,按照誰先下載完成誰先執行的原則進行,所以當它們之間有順序依賴的時候特別容易出錯。 :::info defer
和 async
都只能用於外部指令碼,如果 script
沒有 src 屬性,則會忽略它們。 :::對於如下這段程式碼,當重新整理瀏覽器的時候會發現頁面上馬上顯示出 我是 h1 標籤
,而過幾秒後才載入完動態插入的指令碼,所以可以得出結論:動態插入的指令碼不會阻塞頁面解析。
<!-- 省略了部分內容 --> <script> function loadScript(src) { let script = document.createElement('script') script.src = src document.body.append(script) } loadScript('https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js') </script> <h1>我是 h1 標籤</h1>
動態插入的指令碼在載入完成後會立即執行,這和 async
一致,所以如果需要保證多個插入的動態指令碼的執行順序,則可以設定 script.async = false
,此時動態指令碼的執行順序將按照插入順序執行和 defer
一樣。
在瀏覽器中載入資源涉及到 2 個事件,分別是 DOMContentLoaded
和 onload
,那麼它們之間有什麼區別呢?
onload
:當頁面所有資源(包括 CSS、JS、圖片、字型、視訊等)都載入完成才觸發,而且它是繫結到 window
物件上;DOMContentLoaded
:當 HTML 已經完成解析,並且構建出了 DOM
,但此時外部資源比如樣式和指令碼可能還沒載入完成,並且該事件需要繫結到 document
物件上;一定看到了上面的可能二字,為什麼當 DOMContentLoaded
觸發的時候樣式和指令碼是可能還沒載入完成呢?
當瀏覽器處理一個 HTML 檔案,並在檔案中遇到 <script>
標籤時,就會在繼續構建 DOM
之前執行它。這是一種防範措施,因為指令碼可能想要修改 DOM
,甚至對其執行 document.write
操作,所以 DOMContentLoaded
必須等待指令碼執行結束後才觸發
。以下這段程式碼驗證了這個結論:當指令碼載入完成的時候,Console
面板下才會列印出 DOMContentLoaded
。
<script> document.addEventListener('DOMContentLoaded', () => { console.log('DOMContentLoaded') }) </script> <h1>我是 h1 標籤</h1> <script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
那麼一定是指令碼執行完成後才會觸發 DOMContentLoaded
嘛?答案也是否定的,有兩個例外,對於 async
指令碼和動態指令碼是不會阻塞 DOMContentLoaded
觸發的。
前面已經介紹到 CSS 是不會阻塞 DOM 的解析的,所以理論上 DOMContentLoaded
應該不會等到外部樣式的載入完成後才觸發,這麼分析是對的,用下面程式碼進行測試一翻就知道了:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <script> document.addEventListener('DOMContentLoaded', () => { console.log('DOMContentLoaded') }) </script> <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet"/> </head> <body> <h1>我是 h1 標籤</h1> </body> </html>
測試結果:當樣式還沒載入完成的時候,就已經列印出 DOMContentLoaded
,這和分析的結果是一致的。但是一定是這樣嘛?顯然不一定,這裡有個小坑,(基於上面程式碼)在樣式後面再加上 <script>
標籤的時候,會發現只有等樣式載入完成了才會列印出 DOMContentLoaded
,為什麼會這樣呢?正是因為 <script>
會阻塞 DOMContentLoaded
的觸發,所以當外部樣式後面有指令碼(**async**
** 指令碼和動態指令碼除外)的時候,外部樣式就會阻塞 **DOMContentLoaded**
的觸發**。
<head> <!-- 只顯示了部分內容 --> <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="stylesheet"/> <script></script> </head>
以上就是網頁資源阻塞瀏覽器載入的原理範例解析的詳細內容,更多關於網頁資源阻塞瀏覽器載入的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45