摘要:下面介紹非阻塞加載腳本技術(shù)也就是異步加載。非阻塞加載腳本關(guān)于的一篇好文目前所有瀏覽器都支持屬性,但是和中只有在加載外部腳本時(shí)才會(huì)生效,行內(nèi)腳本使用是沒有作用的。在中,只有外部腳本才會(huì)發(fā)生阻塞。
上篇博客說過腳本后置可以使頁面更快的加載,可是這樣的優(yōu)化還是有限的,如果腳本需要執(zhí)行一個(gè)耗時(shí)的操作,就算后置了它還是會(huì)阻塞后續(xù)腳本加載和執(zhí)行并且阻塞整個(gè)頁面。下面介紹非阻塞加載腳本技術(shù)也就是異步加載。
非阻塞加載腳本1.defer(關(guān)于defer的一篇好文)
目前所有瀏覽器都支持defer屬性,但是Chrome和Firefox中只有在加載外部腳本時(shí)defer才會(huì)生效,行內(nèi)腳本使用defer是沒有作用的。而IE中不論什么情況,defer都有效。
defer的作用就是阻止腳本在下載完成后立刻執(zhí)行,它會(huì)讓腳本延遲到所有腳本加載執(zhí)行完成后,在DOMContentLoaded之前執(zhí)行,通俗的說就是順序加載延遲執(zhí)行。雖然都是在DOMContentLoaded之前執(zhí)行,但是在不同瀏覽器之間,執(zhí)行的各種腳本執(zhí)行的順序還是不一樣的??聪旅孢@個(gè)例子:
運(yùn)行結(jié)果如下:
從上面可以看出幾個(gè)問題:
首先,IE9以下不支持DOMContentLoaded(后面會(huì)說明這個(gè)情況)
其次,驗(yàn)證了上面說的Chrome和Firefox行內(nèi)腳本不支持defer屬性
最后,defer確實(shí)達(dá)到了延遲執(zhí)行的目的,沒有阻塞后面腳本的加載和執(zhí)行。但是耗時(shí)的操作還是會(huì)阻塞DOMContentLoaded事件,而大多數(shù)情況下大家都會(huì)把頁面初始化的腳本附加在DOMContentLoaded事件上,所以defer方法還是不能很好解決這個(gè)問題。
2.Script DOM
這是最常用也是現(xiàn)在普遍的解決方法。它只需要簡(jiǎn)單幾句話就可以實(shí)現(xiàn)腳本的異步加載,并且所有瀏覽器都支持這個(gè)方法。但是在每個(gè)瀏覽器中,執(zhí)行還是略有不同??聪旅孢@個(gè)例子:
運(yùn)行結(jié)果如下:
下面這張圖是在ScriptDom腳本后面加入一個(gè)耗時(shí)的腳本,使得這個(gè)腳本執(zhí)行完成后,保證ScriptDOM的腳本處于可執(zhí)行狀態(tài):
結(jié)果如下:
運(yùn)行結(jié)果同時(shí)也說明了幾個(gè)問題:
首先,ScriptDOM不會(huì)阻塞后續(xù)腳本的執(zhí)行,根據(jù)start和end 的位置可以很容易看出。
其次,在第二張圖的情況下,ScriptDOM和defer同時(shí)都可以執(zhí)行,在不同瀏覽器中它們的優(yōu)先級(jí)的不一樣的。在Firfox和Chrome中,ScriptDOM的優(yōu)先級(jí)比defer低,而在IE中情況則相反。
最后,通過兩種情況的對(duì)比發(fā)現(xiàn),在Chrome中ScriptDOM不會(huì)阻塞DOMContentLoaded事件但是會(huì)阻塞onload事件;在Firefox中ScriptDOM既會(huì)阻塞DOMContentLoaded事件也會(huì)阻塞onload事件;而在IE中,情況則要根據(jù)代碼執(zhí)行情況來決定。如果在DOMContentLoaded事件或者onload事件觸發(fā)之前,ScriptDOM代碼處于可執(zhí)行狀態(tài),那么就會(huì)阻塞兩個(gè)事件;如果在DOMContentLoaded事件或者onload事件觸發(fā)之前,ScriptDOM代碼處于不可執(zhí)行狀態(tài),那么就不會(huì)阻塞兩個(gè)事件。總結(jié)的來說就是在Chrome和IE中DOMContentLoaded事件不需要等待ScriptDOM執(zhí)行,而在Firefox中需要等待ScriptDOM執(zhí)行。
通過上面兩種方法的對(duì)比發(fā)現(xiàn),defer和ScriptDOM都不會(huì)阻塞后續(xù)腳本的執(zhí)行。但是相對(duì)來說,ScriptDOM在使用上更加靈活而且并不總是阻塞DOMContentLoaded事件,并且ScriptDOM的使用場(chǎng)景主要是在按需加載和模塊加載器上,而一般使用這些技術(shù)的時(shí)候,頁面已經(jīng)處于加載完成的狀態(tài),所以對(duì)于性能不會(huì)有影響。
上面說到DOMContentLoaded事件,DOMcontentLoaded是現(xiàn)代瀏覽器才支持的一個(gè)事件,萬惡的IE從IE9開始才支持這個(gè)事件。那么在什么情況下才會(huì)觸發(fā)DOMContentLoaded事件呢?DOMContentLoaded會(huì)在瀏覽器接收到服務(wù)器傳過來的HTML文檔,整個(gè)頁面DOM結(jié)構(gòu)加載完成并且所有行內(nèi)腳本和外部腳本執(zhí)行完成后觸發(fā) (通過上面異步腳本的例子可以看出,ScriptDOM異步加載腳本不會(huì)阻塞DOMContentLoaded,或者說DOMContentLoaded不需要等待ScriptDOM執(zhí)行就可以出發(fā)) ,它跟onload事件的區(qū)別是,DOMContentLoaded事件不需要等待圖片,ifram和樣式表等資源加載完成就會(huì)觸發(fā),而onload事件需要等待整個(gè)頁面都加載完成包括各種資源才會(huì)觸發(fā)。所以對(duì)于我們來說DOMContentLoaded是一個(gè)更有用的事件,因?yàn)橹灰狣OM結(jié)構(gòu)加載完成,我們就可以通過Javasscript來操作頁面上的DOM節(jié)點(diǎn)。
但是上面關(guān)于DOMContentLoaded事件觸發(fā)條件的定義只是官方文檔的說法,具體情況并不總是這樣。
有時(shí)樣式表的加載會(huì)阻塞腳本的執(zhí)行從而阻塞DOMContentLoaded事件,這種情況一般出現(xiàn)在樣式表后面跟著腳本。也就是說如果把腳本放在樣式表后面,那么腳本就必須等到樣式表加載完成才能開始執(zhí)行,這樣就會(huì)阻塞頁面的DOMContentLoaded事件。但是這樣做也是有道理的,因?yàn)橛袝r(shí)候我們的腳本會(huì)處理DOM樣式方面的東西。
這種阻塞情況在不同瀏覽器上表現(xiàn)也會(huì)不一樣。在IE和Firefox中,不管樣式表后面跟著是行內(nèi)腳本還是外部腳本,都會(huì)發(fā)生阻塞。在Chrome中,只有外部腳本才會(huì)發(fā)生阻塞。
由于IE在IE9以下不支持DOMContentLoaded事件,所以我們需要用一些Hack技術(shù)來實(shí)現(xiàn)這個(gè)功能。分兩種情況來實(shí)現(xiàn):
1.網(wǎng)頁不嵌套在iframe中
在IE中我們可以通過一個(gè)方式來判斷DOM是否加載完成,就是doScroll方法。如果DOM加載完成,那么我們就可以調(diào)用document的doScroll方法,否則就會(huì)拋出異常。我們可以利用這個(gè)特性不斷輪詢來做Hack。
function bindReady(handle){ //判斷是否在iframe中 try{ var isFrame = window.frameElement != null ; }catch(e){} if(document.documentElement.doScroll && !isFrame){ //輪詢是否可以調(diào)用doScroll方法 function tryScroll(){ try{ document.documentElement.doScroll("left"); handle() ; }catch(e){ setTimeout(tryScroll,10) ; } } tryScroll() ; } }
2.網(wǎng)頁嵌套在iframe中
如果網(wǎng)頁嵌套在iframe中,那么是無法通過doScroll的方法來Hack實(shí)現(xiàn)DOMContentLoaded的。我們可以通過另外一種方式來實(shí)現(xiàn)---readystatechange,代碼如下:
function bindReady(handle){ document.onreadystatechange = function(){ if(document.readyState === "complete" || document.readyState === "loaded"){ handle() ; } } }
結(jié)合上面的討論,我們可以得出一個(gè)通用的bindReady方法。
//綁定DOMContentLoaded事件,支持綁定多個(gè)處理函數(shù) var handleList = [] ; function onReady(handle){ //按順序執(zhí)行處理函數(shù) var doHandles = function(){ var length = handleList.length ; for(var i = 0 ; i < length ; i ++){ handleList[i]() ; } } if(handleList.length == 0){ //在還沒有處理函數(shù)時(shí),把doHandles注冊(cè)到ready上,這樣后面加入的處理函數(shù)就可以一并執(zhí)行 bindReady(doHandles) ; } //把處理函數(shù)加入到函數(shù)列表中 handleList.push(handle) ; } function bindReady(handle){ var called = false ; var ready = function(){ //防止重復(fù)調(diào)用 if(!called){ called = true ; handle() ; } } if(document.addEventListener){ //支持DOMcontentLoaded document.addEventListener("DOMContentLoaded",ready,false); }else if(document.attachEvent){ //IE try{ var isFrame = window.frameElement != null ; }catch(e){} //網(wǎng)頁不在iframe中 if(document.documentElement.doScroll && !isFrame){ function tryScroll(){ try{ document.documentElement.doScroll("left") ; ready() ; }catch(e){ setTimeout(tryScroll,10) ; } } tryScroll() ; }else{ //網(wǎng)頁在iframe中 document.onreadystatechange = function(){ if(document.readyState === "complete" || document.readyState === "loaded"){ ready() ; } } } } //老式瀏覽器不支持上面兩種事件 if(window.addEventListener){ window.addEventListener("load",ready,false) ; }else if(window.attachEvent){ window.attachEvent("onload",ready) ; }else{ //允許綁定多個(gè)處理函數(shù) var fn = window.onload ; window.onload = function(){ fn && fn() ; ready() ; } } }說在最后
說了這么多,雖然通過腳本后置和異步加載可以降低腳本加載對(duì)頁面的影響,但是就算是實(shí)現(xiàn)了異步加載,但是由于瀏覽器的腳本解析的單線程的,所以腳本執(zhí)行的時(shí)候仍然會(huì)阻塞整個(gè)頁面(當(dāng)然除了使用Web Worker),這時(shí)候用戶是無法完成正常交互的,所以要想真正徹底的優(yōu)化頁面加載,還需要從代碼的優(yōu)化開始。從下一篇開始,我會(huì)分享關(guān)于這方面的學(xué)習(xí)。
最后,安利下我的個(gè)人博客,歡迎訪問: http://bin-playground.top文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78163.html
摘要:需要注意的一點(diǎn)是,面板下的功能,是對(duì)于細(xì)節(jié)中的細(xì)節(jié)進(jìn)行的優(yōu)化。我們可以很清晰明了得分析按照活動(dòng),目錄,域,子域,和進(jìn)行分組的前端性能。個(gè)人理解的話,前者類似事件冒泡,后者類似事件捕獲。同學(xué)在點(diǎn)我達(dá),他們正在籌劃改組成大前端團(tuán)隊(duì)。 對(duì)Chrome控制臺(tái)有一定的了解的朋友都在知道,Network面板會(huì)包括很多網(wǎng)絡(luò)請(qǐng)求方面的東西,包括Http相關(guān)的Request信息,Response信息...
摘要:幸運(yùn)的是,瀏覽器行為的基礎(chǔ)原理是相當(dāng)穩(wěn)定而且文檔齊全的,并且在相當(dāng)長(zhǎng)一段時(shí)間內(nèi)肯定不會(huì)發(fā)生顯著變化。瀏覽器有種稱為預(yù)加載掃描器的東西,它會(huì)掃描的腳本,并開始預(yù)加載腳本,不過腳本只會(huì)在先前的節(jié)點(diǎn)已經(jīng)構(gòu)建完成后,才會(huì)依次執(zhí)行。 本文轉(zhuǎn)載自:眾成翻譯譯者:網(wǎng)絡(luò)埋伏紀(jì)事鏈接:http://www.zcfy.cc/article/2847原文:https://hackernoon.com/opt...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。背后的故事本文是對(duì)于年之間世界發(fā)生的大事件的詳細(xì)介紹,闡述了從提出到角力到流產(chǎn)的前世今生。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎...
摘要:從本篇博客開始,我會(huì)跟大家分享下我關(guān)于前端優(yōu)化方面的學(xué)習(xí),由于時(shí)間原因每篇博客只能分享一小點(diǎn)內(nèi)容,一點(diǎn)點(diǎn)深入前端優(yōu)化的細(xì)節(jié)。在前端優(yōu)化這個(gè)問題上,最被大家熟知的應(yīng)該就是雅虎前端優(yōu)化條軍規(guī)以及雅虎前端優(yōu)化條規(guī)則。 從本篇博客開始,我會(huì)跟大家分享下我關(guān)于前端優(yōu)化方面的學(xué)習(xí),由于時(shí)間原因每篇博客只能分享一小點(diǎn)內(nèi)容,一點(diǎn)點(diǎn)深入前端優(yōu)化的細(xì)節(jié)?! ∽鲞^前端的人都知道,前端優(yōu)化是一個(gè)永遠(yuǎn)都不會(huì)...
摘要:下面我們撇開網(wǎng)絡(luò)方面的優(yōu)化,只分析靜態(tài)資源方面的優(yōu)化。不過,也會(huì)阻止的構(gòu)建和延緩網(wǎng)頁渲染。未優(yōu)化正常加載優(yōu)化后異步加載根據(jù)上面的分析,我們可以清楚的認(rèn)識(shí)到,非必要優(yōu)先加載的,選擇異步加載是最優(yōu)選擇。 為什么做優(yōu)化 經(jīng)典問題:白屏?xí)r間過長(zhǎng),用戶體驗(yàn)差產(chǎn)生的原因:網(wǎng)絡(luò)問題、關(guān)鍵渲染路徑(CRP)問題 怎么做優(yōu)化 如何做好優(yōu)化呢,網(wǎng)上隨便一搜,就有很多優(yōu)化總結(jié),無非就是網(wǎng)絡(luò)優(yōu)化、靜態(tài)資源(h...
閱讀 2287·2021-11-23 09:51
閱讀 5682·2021-09-22 15:39
閱讀 3355·2021-09-02 15:15
閱讀 3506·2019-08-30 15:54
閱讀 2364·2019-08-30 15:53
閱讀 1405·2019-08-30 14:04
閱讀 2459·2019-08-29 18:33
閱讀 2378·2019-08-29 13:08