您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“CSS/JS網(wǎng)頁資源阻塞瀏覽器加載的原理是什么”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
一個(gè)頁面允許加載的外部資源有很多,常見的有腳本、樣式、字體、圖片和視頻等,對于這些外部資源究竟是如何影響整個(gè)頁面的加載和渲染的呢?今天來一探究竟。
如何用 Chrome 定制網(wǎng)絡(luò)加載速度?
圖片/視頻/字體會阻塞頁面加載嗎?
CSS 是如何阻塞頁面加載的?
JS 又是如何阻塞頁面加載的?
JS 一定會阻塞 DOM 加載嗎?
defer 和 async 是什么?又有何特點(diǎn)?
動態(tài)腳本會造成阻塞嗎?
阻塞是怎么和 DOMContentLoaded 與 onload 扯上關(guān)系的?
測試之前需要對瀏覽器下載資源的速度進(jìn)行控制,將它重新設(shè)置為 50kb/s,操作方式:
打開 Chrome 開發(fā)者工具;
在 Network 面板下找到 Disable cache 右側(cè)的下拉列表,然后選擇 Add 添加自定義節(jié)流配置;
添加一個(gè)下載速度為 50kb/s 的配置;
最后在第二步驟中的下拉列表選擇剛剛配置的選項(xiàng)即可;
注意:如果當(dāng)前選擇的自定義選項(xiàng)被修改了,則需要切換到別的選項(xiàng)再切回來才可生效。
為什么是這個(gè)速度?因?yàn)槿缦碌囊恍┵Y源,比如圖片、樣式或者腳本體積都是 50kb 的好幾倍,方便測試。
直接寫個(gè)示例來看下結(jié)果:
<!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> <h2>我是 h2 標(biāo)簽</h2> <img src="https://xxx.oss-cn-shenzhen.aliyuncs.com/images/flow.png" /> <h3>我是 h3 標(biāo)簽</h3> </body> </html>
上面這張圖片的大小大概是 200kb,當(dāng)把網(wǎng)絡(luò)下載速度限制成 50kb/s,打開該頁面,可以看到如下結(jié)果:當(dāng) h2
和 h3
標(biāo)簽渲染出來且打印了 DOMContentLoaded
的時(shí)候,此時(shí)圖片還在加載中,這就說明了圖片并不會阻塞 DOM
的加載,更加不會阻塞頁面渲染;當(dāng)圖片加載完成的時(shí)候,會打印 onload
,說明圖片延遲了 onload
事件的觸發(fā)。
視頻、字體和圖片其實(shí)是一樣的,也不會阻塞 DOM
的加載和渲染。
同樣的,還是直接用代碼來測試 CSS 加載對頁面阻塞的情況,因?yàn)橄旅娲a加載的 bootstrap.css
是 192kb 的,所以理論上下載它應(yīng)該需要花費(fèi) 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> <h2>我是 h2 標(biāo)簽</h2> </body> </html>
測試過程如下:
在 Elements
面板下,選中 h2
這個(gè)標(biāo)簽,然后按 delete
鍵將它從 DOM
中刪掉,從而模擬首次加載;
刷新瀏覽器,馬上 Elements
面板下就加載出 h2
標(biāo)簽,繼續(xù)加載 3 到 4 秒后(此時(shí)正在加載 bootstrap.css),頁面出現(xiàn) 我是 h2 標(biāo)簽
字樣,此時(shí)頁面已經(jīng)渲染完成。
從而得出結(jié)論:
bootstrap.css 還沒加載完成,而 DOM
中就已經(jīng)出現(xiàn) h2 標(biāo)簽,說明 CSS 不會阻塞 DOM 的解析;
頁面直到 bootstrap.css 加載完成才出現(xiàn) h2 里的文案,說明 CSS 會阻塞 DOM 的渲染。
為什么是這個(gè)結(jié)論呢?試想一下頁面渲染的流程就知道了。瀏覽器首先解析 HTML 生成 DOM 樹,解析 CSS
生成 CSSOM
樹,然后 DOM
樹和 CSSOM
樹進(jìn)行合成生成渲染樹,通過渲染樹進(jìn)行布局并且計(jì)算每個(gè)節(jié)點(diǎn)信息,繪制頁面。
可以說解析 DOM 和 解析 CSS 其實(shí)是并列進(jìn)行的,既然是并列進(jìn)行的,那 CSS 和 DOM 就不會互相影響了,這和結(jié)論一相符;另外渲染頁面一定是在得到 CSSOM 樹之后進(jìn)行的,這和結(jié)論二相符。
CSS 一定會阻塞 DOM 的渲染嘛?答案是否定的,當(dāng)把外鏈樣式放到 <body>
最尾部去加載:
<body> <h2>我是 h2 標(biāo)簽</h2> <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>
此時(shí)刷新瀏覽器,頁面上會馬上顯示出 我是 h2 標(biāo)簽
字樣,當(dāng) 3 到 4 秒過后樣式加載完成的時(shí)會造成二次渲染,頁面重新渲染出該字樣,這就說明 CSS 阻塞 DOM 的渲染只阻塞定義在 CSS 后面的 DOM。二次渲染會對用戶造成不好的體驗(yàn)且加重了瀏覽器的負(fù)擔(dān),所以這也就是為什么需要把外鏈樣式提前到 <head>
里加載的原因。
CSS 阻塞了后面 DOM 的渲染,那它會阻塞 JS 的執(zhí)行嘛?
<!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> <h2>我是 h2 標(biāo)簽</h2> <script> console.log('888') </script> </body> </html>
刷新瀏覽器的時(shí)候可以看到,瀏覽器 Console
面板下沒有打印內(nèi)容,而當(dāng)樣式加載完成的時(shí)候打印了 888,這就說明 CSS 會阻塞定義在其之后 JS 的執(zhí)行。
為什么會這樣呢?試想一下,如果 JS 里執(zhí)行的操作需要獲取當(dāng)前 h2 標(biāo)簽的樣式,而由于樣式?jīng)]加載完成,所以就無法得到想要的結(jié)果,從而證明了 CSS 需要阻塞定義在其之后 JS 的執(zhí)行。
CSS 會阻塞 DOM 的渲染和阻塞定義在其之后的 JS 的執(zhí)行,那 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> <h2>我是 h2 標(biāo)簽</h2> </body> </html>
首先刪除頁面中已經(jīng)存在的 h2 標(biāo)簽(如果存在的話),仔細(xì)觀察 Elements 面板,當(dāng)刷新瀏覽器的時(shí)候,一直未加載出 h2 標(biāo)簽(期間頁面一直白屏),直到 JS 加載完成后,DOM 中才出現(xiàn),這足以說明了 JS 會阻塞定義在其之后的 DOM
的加載,所以應(yīng)該將外部 JS 放到 <body>
的最尾部去加載,減少頁面加載白屏?xí)r間。
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> <h2>我是 h2 標(biāo)簽</h2> </body> </html>
上面這段代碼的測試結(jié)果是當(dāng)頁面中顯示出 h2 標(biāo)簽的時(shí)候,腳本還沒有加載完成,這就說明了 async
腳本不會阻塞 DOM 的加載;同理可以用同樣的方式測試 defer
,也會得到這個(gè)結(jié)論。
現(xiàn)在知道了通過 defer
或者 async
方式加載 JS 的時(shí)候,它是不會阻塞 DOM 加載的。知道 defer
和 async
是什么嗎?它們兩者有什么區(qū)別呢?
回答這些疑問之前,先來看下當(dāng)瀏覽器解析 HTML 遇到 script
標(biāo)簽的時(shí)候會發(fā)生什么?
暫停解析 DOM;
執(zhí)行 script
里的腳本,如果該 script
是外鏈,則會先下載它,下載完成后立刻執(zhí)行;
執(zhí)行完成后繼續(xù)解析剩余 DOM。
上面這是解析時(shí)遇到一個(gè)正常的外鏈的情況,正常外鏈的下載和執(zhí)行都會阻塞頁面解析;而如果外鏈?zhǔn)峭ㄟ^ defer
或者 async
加載的時(shí)候又會是如何呢?
defer
特點(diǎn)
對于 defer
的 script
,瀏覽器會繼續(xù)解析 html
,且同時(shí)并行下載腳本,等 DOM
構(gòu)建完成后,才會開始執(zhí)行腳本,所以它不會造成阻塞;
defer
腳本下載完成后,執(zhí)行時(shí)間一定是 DOMContentLoaded
事件觸發(fā)之前執(zhí)行;
多個(gè) defer
的腳本執(zhí)行順序嚴(yán)格按照定義順序進(jìn)行,而不是先下載好的先執(zhí)行;
async
特點(diǎn)
對于 async
的 script
,瀏覽器會繼續(xù)解析 html
,且同時(shí)并行下載腳本,一旦腳本下載完成會立刻執(zhí)行;和 defer 一樣,它在下載的時(shí)候也不會造成阻塞,但是如果它下載完成后 DOM
還沒解析完成,則執(zhí)行腳本的時(shí)候是會阻塞解析的;
async
腳本的執(zhí)行 和 DOMContentLoaded
的觸發(fā)順序無法明確誰先誰后,因?yàn)槟_本可能在 DOM
構(gòu)建完成時(shí)還沒下載完,也可能早就下載好了;
多個(gè) async
,按照誰先下載完成誰先執(zhí)行的原則進(jìn)行,所以當(dāng)它們之間有順序依賴的時(shí)候特別容易出錯(cuò)。 :::info defer
和 async
都只能用于外部腳本,如果 script
沒有 src 屬性,則會忽略它們。 :::
對于如下這段代碼,當(dāng)刷新瀏覽器的時(shí)候會發(fā)現(xiàn)頁面上馬上顯示出 我是 h2 標(biāo)簽
,而過幾秒后才加載完動態(tài)插入的腳本,所以可以得出結(jié)論:動態(tài)插入的腳本不會阻塞頁面解析。
<!-- 省略了部分內(nèi)容 --> <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> <h2>我是 h2 標(biāo)簽</h2>
動態(tài)插入的腳本在加載完成后會立即執(zhí)行,這和 async
一致,所以如果需要保證多個(gè)插入的動態(tài)腳本的執(zhí)行順序,則可以設(shè)置 script.async = false
,此時(shí)動態(tài)腳本的執(zhí)行順序?qū)凑詹迦腠樞驁?zhí)行和 defer
一樣。
在瀏覽器中加載資源涉及到 2 個(gè)事件,分別是 DOMContentLoaded
和 onload
,那么它們之間有什么區(qū)別呢?
onload
:當(dāng)頁面所有資源(包括 CSS、JS、圖片、字體、視頻等)都加載完成才觸發(fā),而且它是綁定到 window
對象上;
DOMContentLoaded
:當(dāng) HTML 已經(jīng)完成解析,并且構(gòu)建出了 DOM
,但此時(shí)外部資源比如樣式和腳本可能還沒加載完成,并且該事件需要綁定到 document
對象上;
一定看到了上面的可能二字,為什么當(dāng) DOMContentLoaded
觸發(fā)的時(shí)候樣式和腳本是可能還沒加載完成呢?
當(dāng)瀏覽器處理一個(gè) HTML 文檔,并在文檔中遇到 <script>
標(biāo)簽時(shí),就會在繼續(xù)構(gòu)建 DOM
之前運(yùn)行它。這是一種防范措施,因?yàn)槟_本可能想要修改 DOM
,甚至對其執(zhí)行 document.write
操作,所以 DOMContentLoaded
必須等待腳本執(zhí)行結(jié)束后才觸發(fā)
。以下這段代碼驗(yàn)證了這個(gè)結(jié)論:當(dāng)腳本加載完成的時(shí)候,Console
面板下才會打印出 DOMContentLoaded
。
<script> document.addEventListener('DOMContentLoaded', () => { console.log('DOMContentLoaded') }) </script> <h2>我是 h2 標(biāo)簽</h2> <script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
那么一定是腳本執(zhí)行完成后才會觸發(fā) DOMContentLoaded
嘛?答案也是否定的,有兩個(gè)例外,對于 async
腳本和動態(tài)腳本是不會阻塞 DOMContentLoaded
觸發(fā)的。
前面已經(jīng)介紹到 CSS 是不會阻塞 DOM 的解析的,所以理論上 DOMContentLoaded
應(yīng)該不會等到外部樣式的加載完成后才觸發(fā),這么分析是對的,用下面代碼進(jìn)行測試一翻就知道了:
<!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> <h2>我是 h2 標(biāo)簽</h2> </body> </html>
測試結(jié)果:當(dāng)樣式還沒加載完成的時(shí)候,就已經(jīng)打印出 DOMContentLoaded
,這和分析的結(jié)果是一致的。但是一定是這樣嘛?顯然不一定,這里有個(gè)小坑,(基于上面代碼)在樣式后面再加上 <script>
標(biāo)簽的時(shí)候,會發(fā)現(xiàn)只有等樣式加載完成了才會打印出 DOMContentLoaded
,為什么會這樣呢?正是因?yàn)?<script>
會阻塞 DOMContentLoaded
的觸發(fā),所以當(dāng)外部樣式后面有腳本(**async**
** 腳本和動態(tài)腳本除外)的時(shí)候,外部樣式就會阻塞 **DOMContentLoaded**
的觸發(fā)**。
<head> <!-- 只顯示了部分內(nèi)容 --> <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>
“CSS/JS網(wǎng)頁資源阻塞瀏覽器加載的原理是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。