1. 程式人生 > >jQuery原始碼解析(架構與依賴模組)

jQuery原始碼解析(架構與依賴模組)

jQuery有3種針對文件載入的方法

$(document).ready(function() {
    // ...程式碼...
})
//document ready 簡寫
$(function() {
    // ...程式碼...
})
$(document).load(function() {
    // ...程式碼...
})

一個是ready一個是load,這兩個到底有什麼區別呢?

ready與load誰先執行:
大家在面試的過程中,經常會被問到一個問題:ready與load那一個先執行,那一個後執行?答案是ready先執行,load後執行。

DOM文件載入的步驟:
要想理解為什麼ready先執行,load後執行就要先了解下DOM文件載入的步驟:

(1) 解析HTML結構。
(2) 載入外部指令碼和樣式表文件。
(3) 解析並執行指令碼程式碼。
(4) 構造HTML DOM模型。//ready
(5) 載入圖片等外部檔案。
(6) 頁面載入完畢。//load

從上面的描述中大家應該已經理解了吧,ready在第(4)步完成之後就執行了,但是load要在第(6)步完成之後才執行。

結論:

ready與load的區別就在於資原始檔的載入,ready構建了基本的DOM結構,所以對於程式碼來說應該越快載入越好。在一個高速瀏覽的時代,沒人願意等待答案。假如一個網站頁面載入超過4秒,不好意思,你1/4的使用者將面臨著流失,所以對於框架來說使用者體驗是至關重要的,我們應該越早處理DOM越好,我們不需要等到圖片資源都載入後才去處理框架的載入,圖片資源過多load事件就會遲遲不會觸發。

我們看看jQuery是如何處理文件載入時機的問題:

jQuery.ready.promise = function( obj ) {
    if ( !readyList ) {
        readyList = jQuery.Deferred();
        if ( document.readyState === "complete" ) {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            setTimeout( jQuery.ready );
        } else {
            document.addEventListener( "DOMContentLoaded", completed, false );
            window.addEventListener( "load", completed, false );
        }
    }
    return readyList.promise( obj );
};

jQuery的ready是通過promise給包裝過的,這也是jQuery擅長的手法,統一了回撥體系,以後我們會重點談到。
可見jQuery相容的具體策略針對高階的瀏覽器,我們當前很樂意用DOMContentLoaded事件了,省時省力。

那麼舊的IE如何處理呢?

繼續看jQuery的方案:

// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", completed );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", completed );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
    top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
    (function doScrollCheck() {
        if ( !jQuery.isReady ) {
            try {
                // Use the trick by Diego Perini
                // http://javascript.nwbox.com/IEContentLoaded/
                top.doScroll("left");
            } catch(e) {
                return setTimeout( doScrollCheck, 50 );
            }
            // detach all dom ready events
            detach();

            // and execute any waiting functions
            jQuery.ready();
        }
    })();
}

    如果瀏覽器存在 document.onreadystatechange 事件,當該事件觸發時,如果 document.readyState=complete 的時候,可視為 DOM 樹已經載入。不過,這個事件不太可靠,比如當頁面中存在圖片的時候,可能反而在 onload 事件之後才能觸發,換言之,它只能正確地執行於頁面不包含二進位制資源或非常少或者被快取時作為一個備選吧。

針對IE的載入檢測

Diego Perini 在 2007 年的時候,報告了一種檢測 IE 是否載入完成的方式,使用 doScroll 方法呼叫,詳情可見http://javascript.nwbox.com/IEContentLoaded/。
原理就是對於 IE 在非 iframe 內時,只有不斷地通過能否執行 doScroll 判斷 DOM 是否載入完畢。在上述中間隔 50 毫秒嘗試去執行 doScroll,注意,由於頁面沒有載入完成的時候,呼叫 doScroll 會導致異常,所以使用了 try -catch 來捕獲異常。
結論:所以總的來說當頁面 DOM 未載入完成時,呼叫 doScroll 方法時,會產生異常。那麼我們反過來用,如果不異常,那麼就是頁面DOM載入完畢了。

這都是我們在第一時間內處理ready載入的問題,如果ready在頁面載入完畢後呢?

jQuery就必須針對這樣的情況跳過綁定了:

if ( document.readyState === "complete" ) {
     // Handle it asynchronously to allow scripts the opportunity to delay ready
     setTimeout( jQuery.ready );
 }

直接通過檢視readyState的狀態來確定頁面的載入是否完成了。這裡會給一個定時器的最小時間後去執行,主要保證執行的正確。