自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

探究網(wǎng)頁(yè)資源究竟是如何阻塞瀏覽器加載的

系統(tǒng) 瀏覽器
一個(gè)頁(yè)面允許加載的外部資源有很多,常見(jiàn)的有腳本、樣式、字體、圖片和視頻等,對(duì)于這些外部資源究竟是如何影響整個(gè)頁(yè)面的加載和渲染的呢?今天我們來(lái)一探究竟。

[[374280]]

一個(gè)頁(yè)面允許加載的外部資源有很多,常見(jiàn)的有腳本、樣式、字體、圖片和視頻等,對(duì)于這些外部資源究竟是如何影響整個(gè)頁(yè)面的加載和渲染的呢?今天我們來(lái)一探究竟。

閱讀完這篇文章你將解開(kāi)如下謎團(tuán):

  • 如何用 Chrome 定制網(wǎng)絡(luò)加載速度?
  • 圖片/視頻/字體會(huì)阻塞頁(yè)面加載嘛?
  • CSS 是如何阻塞頁(yè)面加載的?
  • JS 又是如何阻塞頁(yè)面加載的?
  • JS 一定會(huì)阻塞 DOM 加載嘛?
  • defer 和 async 是什么?又有何特點(diǎn)?
  • 動(dòng)態(tài)腳本會(huì)造成阻塞嘛?
  • 阻塞是怎么和 DOMContentLoaded 與 onload 扯上關(guān)系的?

測(cè)試前環(huán)境準(zhǔn)備

測(cè)試之前我們需要對(duì)瀏覽器下載資源的速度進(jìn)行控制,將它重新設(shè)置為 50kb/s,操作方式:

  1. 打開(kāi) Chrome 開(kāi)發(fā)者工具;
  2. 在 Network 面板下找到 Disable cache 右側(cè)的下拉列表,然后選擇 Add 添加自定義節(jié)流配置;
  3. 添加一個(gè)下載速度為 50kb/s 的配置;
  4. 最后在第二步驟中的下拉列表選擇剛剛配置的選項(xiàng)即可;
  5. 注意:如果當(dāng)前選擇的自定義選項(xiàng)被修改了,則需要切換到別的選項(xiàng)再切回來(lái)才可生效。

為什么是這個(gè)速度?因?yàn)槿缦碌囊恍┵Y源,比如圖片、樣式或者腳本體積都是 50kb 的好幾倍,方便測(cè)試。

圖片會(huì)造成阻塞嘛

直接寫個(gè)示例來(lái)看下結(jié)果:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <script> 
  6.         document.addEventListener('DOMContentLoaded', () => { 
  7.             console.log('DOMContentLoaded'
  8.         }) 
  9.         window.onload = function() { 
  10.             console.log('onload'
  11.         } 
  12.     </script> 
  13. </head> 
  14. <body> 
  15.     <h1>我是 h1 標(biāo)簽</h1> 
  16.     <img src="https://xxx.oss-cn-shenzhen.aliyuncs.com/images/flow.png" /> 
  17.     <h2>我是 h2 標(biāo)簽</h2> 
  18. </body> 
  19. </html> 

上面這張圖片的大小大概是 200kb,當(dāng)把網(wǎng)絡(luò)下載速度限制成 50kb/s,打開(kāi)該頁(yè)面,可以看到如下結(jié)果:當(dāng) h1 和 h2 標(biāo)簽渲染出來(lái)且打印了 DOMContentLoaded 的時(shí)候,此時(shí)圖片還在加載中,這就說(shuō)明了圖片并不會(huì)阻塞 DOM 的加載,更加不會(huì)阻塞頁(yè)面渲染;當(dāng)圖片加載完成的時(shí)候,會(huì)打印 onload,說(shuō)明圖片延遲了 onload 事件的觸發(fā)。

視頻、字體和圖片其實(shí)是一樣的,也不會(huì)阻塞 DOM 的加載和渲染。

CSS 加載阻塞

同樣的,我們還是直接用代碼來(lái)測(cè)試 CSS 加載對(duì)頁(yè)面阻塞的情況,因?yàn)橄旅娲a加載的 bootstrap.css 是 192kb 的,所以理論上下載它應(yīng)該需要花費(fèi) 3 到 4 秒左右。

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" /> 
  6. </head> 
  7. <body> 
  8.     <h1>我是 h1 標(biāo)簽</h1> 
  9. </body> 
  10. </html> 

測(cè)試過(guò)程如下:

  1. 在 Elements 面板下,選中 h1 這個(gè)標(biāo)簽,然后按 delete 鍵將它從 DOM 中刪掉,從而模擬首次加載;
  2. 刷新瀏覽器,馬上 Elements 面板下就加載出 h1 標(biāo)簽,繼續(xù)加載 3 到 4 秒后(此時(shí)正在加載 bootstrap.css),頁(yè)面出現(xiàn) 我是 h1 標(biāo)簽 字樣,此時(shí)頁(yè)面已經(jīng)渲染完成。

從而得出結(jié)論:

  • bootstrap.css 還沒(méi)加載完成,而 DOM 中就已經(jīng)出現(xiàn) h1 標(biāo)簽,說(shuō)明 CSS 不會(huì)阻塞 DOM 的解析;
  • 頁(yè)面直到 bootstrap.css 加載完成才出現(xiàn) h1 里的文案,說(shuō)明 CSS 會(huì)阻塞 DOM 的渲染。

為什么是這個(gè)結(jié)論呢?試想一下頁(yè)面渲染的流程就知道了。瀏覽器首先解析 HTML 生成 DOM樹(shù),解析 CSS 生成 CSSOM 樹(shù),然后 DOM 樹(shù)和 CSSOM 樹(shù)進(jìn)行合成生成渲染樹(shù),通過(guò)渲染樹(shù)進(jìn)行布局并且計(jì)算每個(gè)節(jié)點(diǎn)信息,繪制頁(yè)面。


可以說(shuō)解析 DOM 和 解析 CSS 其實(shí)是并列進(jìn)行的,既然是并列進(jìn)行的,那 CSS 和 DOM 就不會(huì)互相影響了,這和結(jié)論一相符;另外渲染頁(yè)面一定是在得到 CSSOM 樹(shù)之后進(jìn)行的,這和結(jié)論二相符。

CSS 一定會(huì)阻塞 DOM 的渲染嘛?答案是否定的,當(dāng)把外鏈樣式放到 最尾部去加載:

  1. <body> 
  2.     <h1>我是 h1 標(biāo)簽</h1> 
  3.     <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" /> 
  4. </body> 

此時(shí)刷新瀏覽器,頁(yè)面上會(huì)馬上顯示出 我是 h1 標(biāo)簽 字樣,當(dāng) 3 到 4 秒過(guò)后樣式加載完成的時(shí)會(huì)造成二次渲染,頁(yè)面重新渲染出該字樣,這就說(shuō)明 CSS 阻塞 DOM 的渲染只阻塞定義在 CSS 后面的 DOM。二次渲染會(huì)對(duì)用戶造成不好的體驗(yàn)且加重了瀏覽器的負(fù)擔(dān),所以這也就是為什么需要把外鏈樣式提前到 里加載的原因。

CSS 會(huì)阻塞后面 JS 的執(zhí)行嘛

CSS 阻塞了后面 DOM 的渲染,那它會(huì)阻塞 JS 的執(zhí)行嘛?

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet" /> 
  6. </head> 
  7. <body> 
  8.     <h1>我是 h1 標(biāo)簽</h1> 
  9.     <script> 
  10.         console.log('888'
  11.     </script> 
  12. </body> 
  13. </html> 

刷新瀏覽器的時(shí)候可以看到,瀏覽器 Console 面板下沒(méi)有打印內(nèi)容,而當(dāng)樣式加載完成的時(shí)候打印了 888,這就說(shuō)明 CSS 會(huì)阻塞定義在其之后 JS 的執(zhí)行。

為什么會(huì)這樣呢?試想一下,如果 JS 里執(zhí)行的操作需要獲取當(dāng)前 h1 標(biāo)簽的樣式,而由于樣式?jīng)]加載完成,所以就無(wú)法得到想要的結(jié)果,從而證明了 CSS 需要阻塞定義在其之后 JS的執(zhí)行。

JS 加載阻塞

CSS 會(huì)阻塞 DOM 的渲染和阻塞定義在其之后的 JS 的執(zhí)行,那 JS 加載會(huì)對(duì)渲染過(guò)程造成什么影響呢?

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> 
  6. </head> 
  7. <body> 
  8.     <h1>我是 h1 標(biāo)簽</h1> 
  9. </body> 
  10. </html> 

首先刪除頁(yè)面中已經(jīng)存在的 h1 標(biāo)簽(如果存在的話),仔細(xì)觀察 Elements 面板,當(dāng)刷新瀏覽器的時(shí)候,一直未加載出 h1 標(biāo)簽(期間頁(yè)面一直白屏),直到 JS 加載完成后,DOM中才出現(xiàn),這足以說(shuō)明了 JS 會(huì)阻塞定義在其之后的 DOM 的加載,所以應(yīng)該將外部 JS 放到 的最尾部去加載,減少頁(yè)面加載白屏?xí)r間。

defer 和 async

JS 一定會(huì)阻塞定義在其之后的 DOM 的加載嘛?來(lái)測(cè)試一下:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <script async src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> 
  6. </head> 
  7. <body> 
  8.     <h1>我是 h1 標(biāo)簽</h1> 
  9. </body> 
  10. </html> 

上面這段代碼的測(cè)試結(jié)果是當(dāng)頁(yè)面中顯示出 h1 標(biāo)簽的時(shí)候,腳本還沒(méi)有加載完成,這就說(shuō)明了 async 腳本不會(huì)阻塞 DOM 的加載;同理我們可以用同樣的方式測(cè)試 defer,也會(huì)得到這個(gè)結(jié)論。

現(xiàn)在我們知道了通過(guò) defer 或者 async 方式加載 JS 的時(shí)候,它是不會(huì)阻塞 DOM 加載的。那么你知道 defer 和 async 是什么嘛?它們兩者有什么區(qū)別呢?

回答這些疑問(wèn)之前,我們先來(lái)看下當(dāng)瀏覽器解析 HTML 遇到 script 標(biāo)簽的時(shí)候會(huì)發(fā)生什么?

  • 暫停解析 DOM;
  • 執(zhí)行 script 里的腳本,如果該 script 是外鏈,則會(huì)先下載它,下載完成后立刻執(zhí)行;
  • 執(zhí)行完成后繼續(xù)解析剩余 DOM。

上面這是解析時(shí)遇到一個(gè)正常的外鏈的情況,正常外鏈的下載和執(zhí)行都會(huì)阻塞頁(yè)面解析;而如果外鏈?zhǔn)峭ㄟ^(guò) defer 或者 async 加載的時(shí)候又會(huì)是如何呢?


defer 特點(diǎn)

  • 對(duì)于 defer 的 script,瀏覽器會(huì)繼續(xù)解析 html,且同時(shí)并行下載腳本,等 DOM 構(gòu)建完成后,才會(huì)開(kāi)始執(zhí)行腳本,所以它不會(huì)造成阻塞;
  • defer 腳本下載完成后,執(zhí)行時(shí)間一定是 DOMContentLoaded 事件觸發(fā)之前執(zhí)行;
  • 多個(gè) defer 的腳本執(zhí)行順序嚴(yán)格按照定義順序進(jìn)行,而不是先下載好的先執(zhí)行;

asyn 特點(diǎn)

  • 對(duì)于 async 的 script,瀏覽器會(huì)繼續(xù)解析 html,且同時(shí)并行下載腳本,一旦腳本下載完成會(huì)立刻執(zhí)行;和 defer 一樣,它在下載的時(shí)候也不會(huì)造成阻塞,但是如果它下載完成后 DOM 還沒(méi)解析完成,則執(zhí)行腳本的時(shí)候是會(huì)阻塞解析的;
  • async 腳本的執(zhí)行 和 DOMContentLoaded 的觸發(fā)順序無(wú)法明確誰(shuí)先誰(shuí)后,因?yàn)槟_本可能在 DOM 構(gòu)建完成時(shí)還沒(méi)下載完,也可能早就下載好了;
  • 多個(gè) async,按照誰(shuí)先下載完成誰(shuí)先執(zhí)行的原則進(jìn)行,所以當(dāng)它們之間有順序依賴的時(shí)候特別容易出錯(cuò)。

defer 和 async 都只能用于外部腳本,如果 script 沒(méi)有 src 屬性,則會(huì)忽略它們。

動(dòng)態(tài)腳本會(huì)造成阻塞嘛

對(duì)于如下這段代碼,當(dāng)刷新瀏覽器的時(shí)候會(huì)發(fā)現(xiàn)頁(yè)面上馬上顯示出 我是 h1 標(biāo)簽,而過(guò)幾秒后才加載完動(dòng)態(tài)插入的腳本,所以可以得出結(jié)論:動(dòng)態(tài)插入的腳本不會(huì)阻塞頁(yè)面解析。

  1. <!-- 省略了部分內(nèi)容 --> 
  2. <script> 
  3.     function loadScript(src) { 
  4.         let script = document.createElement('script'
  5.         script.src = src 
  6.         document.body.append(script) 
  7.     } 
  8.     loadScript('https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js'
  9. </script> 
  10. <h1>我是 h1 標(biāo)簽</h1> 

動(dòng)態(tài)插入的腳本在加載完成后會(huì)立即執(zhí)行,這和 async 一致,所以如果需要保證多個(gè)插入的動(dòng)態(tài)腳本的執(zhí)行順序,則可以設(shè)置 script.async = false,此時(shí)動(dòng)態(tài)腳本的執(zhí)行順序?qū)凑詹迦腠樞驁?zhí)行和 defer 一樣。

DOMContentLoaded 和 onload

在瀏覽器中加載資源涉及到 2 個(gè)事件,分別是 DOMContentLoaded 和 onload,那么它們之間有什么區(qū)別呢?

  • onload:當(dāng)頁(yè)面所有資源(包括 CSS、JS、圖片、字體、視頻等)都加載完成才觸發(fā),而且它是綁定到 window 對(duì)象上;
  • DOMContentLoaded:當(dāng) HTML 已經(jīng)完成解析,并且構(gòu)建出了 DOM,但此時(shí)外部資源比如樣式和腳本可能還沒(méi)加載完成,并且該事件需要綁定到 document 對(duì)象上;

細(xì)心的你一定看到了上面的可能二字,為什么當(dāng) DOMContentLoaded 觸發(fā)的時(shí)候樣式和腳本是可能還沒(méi)加載完成呢?

DOMContentLoaded 遇到腳本

當(dāng)瀏覽器處理一個(gè) HTML 文檔,并在文檔中遇到

  1. <script> 
  2.     document.addEventListener('DOMContentLoaded', () => { 
  3.         console.log('DOMContentLoaded'
  4.     }) 
  5. </script> 
  6. <h1>我是 h1 標(biāo)簽</h1> 
  7. <script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script> 

那么一定是腳本執(zhí)行完成后才會(huì)觸發(fā) DOMContentLoaded 嘛?答案也是否定的,有兩個(gè)例外,對(duì)于 async 腳本和動(dòng)態(tài)腳本是不會(huì)阻塞 DOMContentLoaded 觸發(fā)的。

DOMContentLoaded 遇到樣式

前面我們已經(jīng)介紹到 CSS 是不會(huì)阻塞 DOM 的解析的,所以理論上 DOMContentLoaded 應(yīng)該不會(huì)等到外部樣式的加載完成后才觸發(fā),這么分析是對(duì)的,讓我們用下面代碼進(jìn)行測(cè)試一翻就知道了:

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.     <meta charset="UTF-8"/> 
  5.     <script> 
  6.         document.addEventListener('DOMContentLoaded', () => { 
  7.         console.log('DOMContentLoaded'
  8.     }) 
  9.     </script> 
  10.     <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet"/> 
  11. </head> 
  12. <body> 
  13.     <h1>我是 h1 標(biāo)簽</h1> 
  14. </body> 
  15. </html> 

測(cè)試結(jié)果:當(dāng)樣式還沒(méi)加載完成的時(shí)候,就已經(jīng)打印出 DOMContentLoaded,這和我們分析的結(jié)果是一致的。但是一定是這樣嘛?顯然不一定,這里有個(gè)小坑,(基于上面代碼)在樣式后面再加上 <script> 標(biāo)簽的時(shí)候,會(huì)發(fā)現(xiàn)只有等樣式加載完成了才會(huì)打印出 DOMContentLoaded,為什么會(huì)這樣呢?正是因?yàn)?<script> 會(huì)阻塞 DOMContentLoaded 的觸發(fā),所以當(dāng)外部樣式后面有腳本(async 腳本和動(dòng)態(tài)腳本除外)的時(shí)候,外部樣式就會(huì)阻塞 DOMContentLoaded 的觸發(fā)。

  1. <!-- 只顯示了部分內(nèi)容 --> 
  2. <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet"/> 
  3. <script></script> 
  4. </head> 

參考文章:

DOMContentLoaded

https://html.spec.whatwg.org/multipage/scripting.html

 

責(zé)任編輯:姜華 來(lái)源: 大海我來(lái)了
相關(guān)推薦

2019-07-22 15:29:53

JavaScriptGitHub語(yǔ)言

2019-06-04 14:15:08

JavaScript V8前端

2019-04-26 13:55:02

Istio微服務(wù)架構(gòu)

2021-01-05 09:23:49

網(wǎng)頁(yè)端消息

2011-02-16 16:13:40

Debian

2011-02-28 09:51:43

內(nèi)省

2018-07-05 16:15:26

緩存數(shù)據(jù)cache miss

2010-08-24 09:19:59

2020-06-11 09:18:34

動(dòng)靜分離架構(gòu)架構(gòu)設(shè)計(jì)開(kāi)發(fā)

2017-11-21 14:56:59

2011-11-19 15:55:41

虛擬化存儲(chǔ)虛擬化日立數(shù)據(jù)

2011-05-30 20:51:49

2021-02-19 20:38:01

互聯(lián)網(wǎng)衛(wèi)星系統(tǒng)

2022-06-13 09:51:35

UWB超寬帶無(wú)線載波通信技術(shù)

2012-05-28 22:49:50

PureView

2011-08-04 13:24:28

IT運(yùn)維

2015-08-26 09:54:19

物聯(lián)網(wǎng)

2016-06-17 12:31:10

Spark SQL數(shù)據(jù)處理Spark

2015-09-29 09:47:14

2023-12-26 01:24:45

Jedis連接池參數(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)