再談DOMContentLoaded與渲染阻塞—分析html頁面事件與資源加載

瀏覽器的多線程中,有的線程負(fù)責(zé)加載資源,有的線程負(fù)責(zé)執(zhí)行腳本,有的線程負(fù)責(zé)渲染界面,有的線程負(fù)責(zé)輪詢、監(jiān)聽用戶事件。

成都創(chuàng)新互聯(lián)公司始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營理念,通過多達(dá)十多年累計(jì)超上千家客戶的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的全網(wǎng)推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶,其中包括:宣傳片制作等企業(yè),備受客戶表揚(yáng)。

這些線程,根據(jù)瀏覽器自身特點(diǎn)以及web標(biāo)準(zhǔn)等等,有的會被瀏覽器特意的阻塞。兩個很明顯的阻塞就是:腳本執(zhí)行時對其他線程的阻塞和腳本加載時對其他線程的阻塞。

這兩個阻塞發(fā)生在HTML頁面初次解析時,它們對性能的影響較大,原因是:

document對象綁定了一個事件:DOMContentLoaded。這個事件會在DOM解析完成之后觸發(fā)。這個事件觸發(fā)之后(而不是window.load事件),會進(jìn)入異步事件驅(qū)動階段(另一個線程控制)。也就是說,DOM解析工作不完成,用戶與頁面的很多(并不是所有)事件交互就無法進(jìn)行。這時候?yàn)g覽器的忙指示(那個頁面上方的煩人的旋轉(zhuǎn)的圓圈)不會消失。

DOMContentLoaded什么時候觸發(fā)?

DOMContentLoaded事件本身不會等待CSS文件、圖片、iframe加載完成。

DOMContentLoaded的觸發(fā)時機(jī)是:加載完頁面,解析完所有標(biāo)簽(不包括執(zhí)行CSS和JS),但是JS的執(zhí)行,需要等待位于它前面的CSS加載(如果是外聯(lián)的話)、執(zhí)行完成,因?yàn)镴S可能會依賴位于它前面的CSS計(jì)算出來的樣式。所以:

  • 如果頁面中沒有script標(biāo)簽,DOMContentLoaded事件并沒有等待CSS文件、圖片加載完成。

  • 如果頁面中靜態(tài)的寫有script標(biāo)簽,DOMContentLoaded事件需要等待JS執(zhí)行完才觸發(fā)。而且script標(biāo)簽中的JS需要等待位于其前面的CSS的加載完成。

注:現(xiàn)代瀏覽器會并發(fā)的預(yù)加載CSS、JS、IMG(例如:當(dāng) HTML 解析器(HTML Parser)被腳本阻塞時,解析器雖然會停止構(gòu)建 DOM,但仍會識別該腳本后面的資源,并進(jìn)行預(yù)加載)。但是,執(zhí)行CSS和JS的順序還是按原來的依賴順序(JS的執(zhí)行要等待位于其前面的CSS和JS加載、執(zhí)行完)——先加載完成的資源,如果其依賴還沒加載、執(zhí)行完,就只能等著。

再談DOMContentLoaded與渲染阻塞—分析html頁面事件與資源加載

所以就造成外部資源阻塞渲染,如CSS 與 JavaScript

  • 默認(rèn)情況下,CSS 被視為阻塞渲染的資源,這意味著瀏覽器將不會渲染任何已處理的內(nèi)容,直至 CSSOM 構(gòu)建完畢。

  • JavaScript 不僅可以讀取和修改 DOM 屬性,還可以讀取和修改 CSSOM 屬性。

默認(rèn)情況下,CSS 被視為阻塞渲染的資源,存在阻塞的 CSS 資源時,瀏覽器會延遲 JavaScript 的執(zhí)行和 DOM 構(gòu)建,這意味著瀏覽器將不會渲染任何已處理的內(nèi)容,直至 CSSOM 構(gòu)建完畢。

總結(jié)如下:

  • css加載不會阻塞DOM樹的解析

  • css加載會阻塞DOM樹的渲染

  • css加載會阻塞后面js語句的執(zhí)行

css會阻塞js,同理,css也會阻塞img解碼、paint(瀏覽器認(rèn)為你的CSS沒有加載完畢,不確定圖片的樣式到底如何,牽扯到重繪資源問題),js不會阻塞img的解碼、paint(估計(jì)chrome做了優(yōu)化,具體本人還不知,希望客官補(bǔ)充)。

css阻塞優(yōu)化:

  • 還可以用媒體類型(media type)和媒體查詢(media query)來解除對渲染的阻塞。

    media=“print",會加載,但不會阻塞;media="(min-width:320px)",會在符合查詢條件下阻塞(適配css會執(zhí)行)

  • 大css文件拆分成多個小css文件,并發(fā)加載

因?yàn)殇秩揪€程和js線程與資源進(jìn)行加載的線程并不互斥,不會互斥意味著:資源的加載可以和UI渲染、重排,事件響應(yīng),或者JavaScript代碼的執(zhí)行的并發(fā)進(jìn)行。

所以資源加載器線程會一直進(jìn)行并發(fā)加載。

這里還有一個知識點(diǎn):下載的最大并行數(shù)指的是從一個主機(jī)上下載的最大并行數(shù),如果從多個主機(jī)下載資源,這個數(shù)量會翻倍,但是由于對DNS的解析也是一個性能優(yōu)化的點(diǎn),故而一般策略是:不應(yīng)設(shè)置超過4個主機(jī),最好只設(shè)置2個主機(jī)。

但是操蛋的就是,如果瀏覽器解析DOM時需要下載腳本資源,那么下載這個資源的線程就是阻塞其他下載線程以及渲染線程,導(dǎo)致渲染速度變慢。

但是假設(shè)該腳本下載的速度較慢,而且多個腳本非并發(fā)下載,并且假如多個<script>內(nèi)腳本執(zhí)行時間較長的話,DOM解析工作還是會一直完不成。

故而我們需要無阻塞加載腳本的技術(shù)。

js阻塞優(yōu)化

因?yàn)椋耗_本執(zhí)行和渲染DOM的并發(fā)可能會引發(fā)嚴(yán)重的沖突(腳本可以修改DOM)

所以:JavaScript引擎和渲染引擎所在的兩個線程被設(shè)計(jì)為互斥的!

這就意味著:在執(zhí)行<script>中內(nèi)容時,瀏覽器會切換到JavaScript引擎所在的線程,此時渲染引擎所在的線程會阻塞,故其后元素的解析和渲染會暫停。這時候如果腳本執(zhí)行時間太長的話,不僅后面的元素會一直看不到,對DOM的解析工作也會一直完不成。用戶會陷入焦急的等待中。

為了防止javascript阻塞,我們會

1、把<script>放到緊跟</body>之前的位置

        這樣就不會影響需要放到頁面上的UI元素的解析了。這樣的好處就是,用戶能即使看到頁面上的UI元素,而防止出現(xiàn)了瀏覽器白屏等現(xiàn)象。

2、動態(tài)腳本元素-不重要的js動態(tài)插入。

        因?yàn)閐ocument.createElement("script")的async屬性默認(rèn)為true,而document.head.appendChild代碼之后,由于沒有觸發(fā)渲染樹的重繪,切換回的渲染線程會將剩下的DOM解析并渲染完畢。同時新插入的<script>中的資源也會并發(fā)的下載。

var script=document.createElement("script");
console.log(script.async);//true

        同理:用XHR對象下載代碼,并注入到頁面也可以達(dá)到同樣的效果

        如果需要同步執(zhí)行,需要將async屬性設(shè)置為fasle

3、h6時代,script添加defer或asyn兩個屬性(html4.0中定義了defer;html5.0中定義了async)

  • 如果 script 標(biāo)簽中包含 defer,那么這一塊腳本將不會影響 HTML 文檔的解析,而是等到 HTML 解析完成后才會執(zhí)行。而 DOMContentLoaded 只有在 defer 腳本執(zhí)行結(jié)束后才會被觸發(fā)。即:整個 document 解析完畢且 defer-script 也加載完成之后(這兩件事情的順序無關(guān)),會執(zhí)行所有由 defer-script 加載的 JavaScript 代碼,然后觸發(fā) DOMContentLoaded 事件。defer不會改變script中代碼執(zhí)行順序

  • 如果 script 標(biāo)簽中包含 async,則 HTML 文檔構(gòu)建不受影響,不需要等待 async-script 執(zhí)行。但是,async-script 加載完成后,就會立即執(zhí)行!如果頁面還是沒有解析完成,就會停下來(阻塞頁面)等此腳本執(zhí)行完畢再繼續(xù)解析。async-script 可能在 DOMContentLoaded 觸發(fā)之前或之后執(zhí)行,但一定在 load 觸發(fā)之前執(zhí)行。而且:多個 async-script 的執(zhí)行順序是不確定的。

document.readyState

說道DOMContentLoaded,不得不提readystatechange,通過document.readyState值來更進(jìn)一步來判斷文檔狀態(tài):

  1. uninitiated:xml 對象被產(chǎn)生,但沒有任何文件被加載。

  2. loading:document正在下載,文件尚未開始解析。

  3. loaded:部分的文件已經(jīng)加載且進(jìn)行解析,但對象模型尚未生效。

  4. interactive:document完成了解析,但是資源還在下載,對象模型是有效但只讀的。

  5. complete:代表加載成功,文檔加載完成,并且所有resource都加載完畢

通過下面代碼驗(yàn)證,在chrome上貌似只有  interactivecomplete。

document.addEventListener("DOMContentLoaded",function () {
    console.log("DOMContentLoaded"+new Date())
});
        document.addEventListener("readystatechange",function () {
            console.log("B_____"+new Date());
            console.log(document.readyState)
//            switch (document.readyState){
//                case "loading":
//                    console.log("LOADING"+new Date());
//                    break;
//                case "loaded":
//                    console.log("loaded"+new Date());
//                    break;
//                case "interactive":
//                    console.log("interactive"+new Date());
//                    break;
//                case "complete":
//                    console.log("complete"+new Date());
//                    break;
//            }
        });

        console.time("A")

A: 5.89208984375ms

B_____Thu May 17 2018 10:23:36 GMT+0800 (CST)

interactive

DOMContentLoadedThu May 17 2018 10:23:36 GMT+0800 (CST)

 B_____Thu May 17 2018 10:23:36 GMT+0800 (CST)

 complete

但是,今天看了:你不知道的 DOMContentLoaded

這里又有疑問:interactive DOMContentLoaded   complete onload三個先后順序是什么呢?

DOMContentLoaded和interactive:表示文檔解析完成,且資源未完全加載完成。區(qū)別呢?執(zhí)行順序呢?

驗(yàn)證表明:interactive 》DOMContentLoaded 》 complete 》 onload

但是,DOMContentLoaded觸發(fā)時候,document.readyState一般是interactive,也有可能complete。而當(dāng)頁面有大量的二進(jìn)制文件(頁面加載的時長大于阻塞的時長的時候),document.readyState=complete 可能反而在 onload 事件之后才能觸發(fā)(這個我未完成驗(yàn)證出這種情況)

我覺得onreadystatechange這個不是很靠譜,一般用DOMContentLoaded判斷頁面解析完全。希望哪位大牛提供這方面的補(bǔ)充,感激不盡!

在圖片上,也有onload跟complete

document.getElementById('load').onclick = function() {  
    var img = new Image();  
    if(img.complete) {  
        console.log('dd');  
    }  
    img.onload = function() {  
        console.log('ff')  
    }  
    img.src="images/1-logo.png";  
  
}

這里順帶提下img加載相關(guān) 屬性

  • onload:表示加載好,換言之,沒有加載好不會執(zhí)行;

  • onAbort:圖片加載的時候,用戶通過點(diǎn)擊停止加載時出發(fā)

  • onerror:如果圖片不存在(網(wǎng)絡(luò)很不通暢,也可能觸發(fā) onerror事件)

  • complete:圖片顯示出來以后為true,


參考文章:

你不知道的 DOMContentLoaded

JS、CSS以及img對DOMContentLoaded事件的影響

瀏覽器線程阻塞和無阻塞加載腳本的理解

css加載會造成阻塞嗎?


轉(zhuǎn)載請注明來源:再談DOMContentLoaded與渲染阻塞—分析html頁面事件與資源加載

網(wǎng)頁標(biāo)題:再談DOMContentLoaded與渲染阻塞—分析html頁面事件與資源加載
網(wǎng)站網(wǎng)址:http://muchs.cn/article4/ghhiie.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、軟件開發(fā)、營銷型網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)