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