JQuery源碼解析-Dom加載過程

下面的幾個工具方法都是和dom加載有關,因此先從dom加載開始。jquery

用到的方法:promise

  isReady:DOM是否已經加載完(內部使用)瀏覽器

  readyWait():等待多少文件的計時器(內部使用)緩存

  holdReady()::推遲DOM觸發dom

  ready():準備DOM觸發異步

  jQuery.ready.promise = function( obj ) {}檢測dom的異步操做async

先看一下jQuery和原生js加載方式有什麼不一樣:工具

     $(function () {
        });
        window.onload = function () {
        };

jQuery是等待頁面中全部的dom加載完畢,而原生JavaScript的onload方法,則是等待頁面全部的元素加載完畢,this

例如:一個img標籤,jQuery只等這個img標籤加載完畢,沒必要等到src中所引用的圖片加載完畢,但onload方法,則必須等到src中的圖片加載完畢才能夠。spa

下面看一下jQuery的加載流程圖:

前面已經說過  $(function () {})和rootjQuery.ready( selector )是相等的,也就是說$(function () {})這種形式寫法,其實最後也被轉換成rootjQuery.ready( selector )。

而rootjQuery.ready( selector )這個實例方法裏:

ready: function( fn ) {
        // Add the callback
        jQuery.ready.promise().done( fn );

        return this;
    },

又是調用的工具方法中的jQuery.ready.promise().done( fn ),因此先從這個方法開始分析,代碼以下:

jQuery.ready.promise = function( obj ) {
    if ( !readyList ) {

        readyList = jQuery.Deferred();

        // Catch cases where $(document).ready() is called after the browser event has already occurred.
        // we once tried to use readyState "interactive" here, but it caused issues like the one
        // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
        if ( document.readyState === "complete" ) {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            setTimeout( jQuery.ready );

        } else {

            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", completed, false );

            // A fallback to window.onload, that will always work
            window.addEventListener( "load", completed, false );
        }
    }
    return readyList.promise( obj );
};

這個方法中有判斷:

首先判斷若是document.readyState === "complete" ,那麼已經表明了在運行到這時,頁面中的dom已經加載完畢了,而後運行setTimeout( jQuery.ready )

這裏用到了setTimeout方法,是爲了兼容ie的hack方法,具體緣由能夠看一下注釋中的網址。

若是readyState 不等於complete,那麼就會添加兩個回調方法,這裏添加兩個的緣由,是由於在火狐等瀏覽器中,會緩存load方法,因此就會先調用load方法,因此這裏添加了兩個回調,保證了在第一時間內調用complete方法。

下面在看一下complete方法中寫了什麼:

// The ready event handler and self cleanup method
    completed = function() {
        document.removeEventListener( "DOMContentLoaded", completed, false );
        window.removeEventListener( "load", completed, false );
        jQuery.ready();
    };

經過源碼能夠看到,這裏不管哪一種方法進行了回調,都會在這裏將事件解綁,因此這裏也就保證了只回調一次,最後又調用了jQuery.ready()方法。

這也說明,其實在jQuery.ready.promise方法中,不管條件如何,都會調用jQuery.ready()方法。

在說ready()方法前,先來講明一下holdReady方法。

這個方法是阻塞dom加載的,看一下這個例子:

a.js:

alert(1);

調用頁面:

$.getScript("a.js", function () {
        })
 $(function () {
  alert(2);
});

這時運行,查看結果發現,其實先彈出的是2,而後纔是1,這顯然和咱們的預期不一致,通常咱們在頁面中引用js的時候,都是想使用這個js文件,而後在進行下面的操做。

但由於是異步,因此當前的結果代表,是最後才運行了a.js中的內容。

因此這時就用到了holdReady方法:

            $.holdReady(true);

            $.getScript("a.js", function () {
                 $.holdReady(false)
            })
            $(function () {
               
                alert(2);
            });

這時頁面的運行結果就正確了。

源碼:

// Is the DOM ready to be used? Set to true once it occurs.
    isReady: false,

    // A counter to track how many items to wait for before
    // the ready event fires. See #6781
    readyWait: 1,

    // Hold (or release) the ready event
    holdReady: function( hold ) {
        if ( hold ) {
            jQuery.readyWait++;
        } else {
            jQuery.ready( true );
        }
    },

能夠看到,方法內部有個計數器,這也就說明了,當頁面加載多個js文件時,能夠進行屢次調用,保證全部文件都加載完畢,才能夠繼續運行。

若是傳入的參數爲false,則執行ready方法,並傳入true:

ready: function( wait ) {

        // Abort if there are pending holds or we're already ready
        if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
            return;
        }

        // Remember that the DOM is ready
        jQuery.isReady = true;

        // If a normal DOM Ready event fired, decrement, and wait if need be
        if ( wait !== true && --jQuery.readyWait > 0 ) {
            return;
        }

        // If there are functions bound, to execute
        readyList.resolveWith( document, [ jQuery ] );

        // Trigger any bound ready events
        if ( jQuery.fn.trigger ) {
            jQuery( document ).trigger("ready").off("ready");
        }
    },

第一個判斷,若是爲true,則將計數器減一,不然判斷isReady變量

第二個判斷,若是計數器還大於0的話,則繼續等待。

到readyList.resolveWith( document, [ jQuery ] ); 這句代碼,就開始運行延時的方法了,第一個參數指定節點,第二個是將jQuery傳入

能夠經過下面的代碼查看:

 $(function (arg) {
                console.log(this);
                console.log(arg);
            })

運行結果能夠看到 this:document arg:jQuery對象。

下面看最後一段代碼

        // Trigger any bound ready events
        if ( jQuery.fn.trigger ) {
            jQuery( document ).trigger("ready").off("ready");
        }

這段代碼是考慮到另外一種加載方式:

 $(document).on('ready', function () {
                alert(1);
            })

若是將這段代碼註釋,能夠看到,這種方式就沒法運行了。

因此這也說明了,jQuery其實有三種寫法:

         $(function () {})
            $(document).ready(function () { })
            $(document).on('ready', function () {})
相關文章
相關標籤/搜索