jQuery源碼分析系列(39) : 動畫隊列

data函數在jQuery中只有短短的300行代碼,很是不起點 ,剖析源碼的時候你會發現jQuery只要在有須要保存數據的地方無時無刻不依賴這個基礎設施node

動畫會調用隊列,隊列會調用data數據接口還保存隊列裏面的的動畫數據jquery

因此咱們在自習回顧下關於數據緩存promise

//These may be used throughout the jQuery core codebase
//存數據的
//用戶使用
data_user = new Data();
//存儲對象
//jQuery內部私有
//用來存事件的, 如click事件那種
data_priv = new Data();

data在jQuery中有兩種緩存

一個是用來存數據的, 對應的對象分別是less

存儲對象: data_user

獲取與設置方法: $.data(el, key, value)

另外一個是用來存事件的,動畫數據的dom

存儲對象: data_priv

獲取與設置方法: $._data(el, key, value)

data_userdata_priv, 就如其名, 一個是用戶用的, 一個是jQuery私有的, 他們都是一個叫Data的實例對象ide

 


爲何要設置這2個數據接口類?函數

jQuery的設置都是維護一個jQuery的數據對象,因此動畫

只是實現data的接口,好比:ui

$('#aaron').data('key', 'value')

實現鏈式的.data接口,這種很簡單,咱們能夠把簡單的數據緩存到jquery內部的這個對象上

方法能夠這樣實現

直接在實例上fn的接口上擴充

$.fn.data = function(k, v) {
    return this.each(function() {
        this[key] = v //把接口data的value保存在dom對象上
    })
    return this
}

這個很簡單把數據直接保存在dom對象上

固然針對基本類型這樣處理固然也是能夠的,若是是引用類型,函數對象呢?這樣處理能夠嗎?

$('#aaron').data('nodeValue', '11111')

$('#aaron').data('key', function(){
    //操做
})

問題來了:

1:這樣會更改DOM自己的屬性值,固然能不能生效還不說

2:傳遞但是引用類型哦,不靠譜的內存回收,說不定就溢出了

3:數據暴露,容易被直接改寫

 

如何解決這些問題? jQuery就引入了數據對象這個概念

 


先無論內部怎麼實現,先看節點的屬性

image

多了一個灰色的自定義的key與value

灰色意味着不能經過for枚舉出來,這種設置在ES5中,是有API直接支持了

// If not, create one
if (!unlock) {
    unlock = Data.uid++;
    // Secure it in a non-enumerable, non-writable property
    try {
        descriptor[this.expando] = {
            value: unlock
        };
        Object.defineProperties(owner, descriptor);
        // Support: Android < 4
        // Fallback to a less secure definition
    } catch (e) {
        descriptor[this.expando] = unlock;
        jQuery.extend(owner, descriptor);
    }
}

看註釋就清晰了,這個屬性是受保護的,不能被改寫

OK了。經過這樣一個惟一的橋接標誌,咱們能夠作一個ORM的映射了,讓dom與一個數據緩存接口產生一一對應的關係

 

 


動畫隊列用到的數據緩存

jQuery爲了實現動畫隊列的鏈式調用,因此必須先在實例就是原型上先擴展一個方法啊,而後在內部才能調用底層的方法

固然jQuery基本全部的層次都是這樣的結構,除了鏈式以外,還能夠把具體的實例方法與原型方法通用

//靜態
jQuery.extend
//實例
.extend

 

動畫的實例方法

this.queue(optall.queue, doAnimation);

調用實例方法中基於動畫的擴展接口

jQuery.fn.extend({
    queue
    dequeue
    delay
    clearQueue
    promise

 

queue: function(type, data) {
    var setter = 2;
    //修正type, 默認爲表示jquery動畫的fx, 若是不爲"fx", 
    //即爲本身的自定義動畫, 通常咱們用"fx"就足夠了.  
    if (typeof type !== "string") {
        data = type;
        type = "fx";
        setter--;
    }

    //只有動畫的回調
    // div.slideToggle(1000);
    // div.slideToggle("fast");
    // div.animate({left:'-=200'},1500);
    // div.queue('fx')
    if (arguments.length < setter) {
        return jQuery.queue(this[0], type);
    }

    return data === undefined ?
        this :
        this.each(function() {
            //調用基礎隊列
            //設置動畫隊列緩存
            //並返回隊列總數
            var queue = jQuery.queue(this, type, data);

            // ensure a hooks for this queue
            jQuery._queueHooks(this, type);

            //直接執行動畫隊列
            //防止在執行函數的時候, 這裏又進行dequeue操做, 這樣會同時執行2個函數, 隊列就不受控制了.  
            if (type === "fx" && queue[0] !== "inprogress") {
                //若是隊列沒有被鎖住, 即此時沒有在執行dequeue. 移出隊列裏第一個函數並執行它.  
                jQuery.dequeue(this, type);
            }
        });
},

實例的queue接口只是作了2個狀況的判斷,一種是傳遞fx,一種是設置隊列,底層仍是經過jQuery.queue靜態方法處理的

 


分析一組動畫:

div.show(1000);
div.hide(2000)
div.show(3000)

1 有時間參數的接口,是確定須要執行動畫的,同一個接口上有多個動畫接口,那麼就會意味着須要隊列來管理執行順序

2 管理隊列引入數據緩存,緩存須要載體node節點,因此動畫的擴展的接口在最上層設計是能夠直接跟DOM鏈式

邏輯上確定是線性的執行,show執行完畢,而後取出hide執行,完畢後在取出show執行

 

那麼動畫的調用是如何組織的?

理論上3個動畫,在cache中有3個緩存的data咱們查看下

爲了便於查看,我把代碼改了下 增長了一個標示 Aaron 對應的是時間

在執行div.show(3000)的時候,咱們查看下緩存

image

發現0位置的div.show(1000);被inprogress給替代了

能夠猜想下第一個動畫已經在開始執行了,那麼它在隊列中會用一個佔位符用來通知後面,我這個動畫還在進行,後面的動畫先等等

 

inprogress」進程鎖是這樣工做的:

若是是dequeue操做, 去掉鎖, 執行隊列裏的函數, 同時給隊列加上鎖. 若是是queue操做, 要看鎖的狀態, 若是被鎖上了, 就只執行隊列的添加操做. 再也不調用dequeue.其實dequeue和queue均可以執行隊列裏的第一個函數.queue操做添加完隊列以後, 會調用dequeue方法去執行函數.

用dequeue執行函數的時候, 這時候若是又用queue觸發dequeue的話, 極可能同時有2個函數在執行. 隊列就失去一大半意義了(仍是能夠保證順序, 可是2個動畫會同時執行).不過這個鎖只能保證在dequeue的時候, 不被queue操做意外的破壞隊列.

若是人爲的同時用2個dequeue, 仍是會破壞動畫效果的. 因此要把fn寫在回調函數裏

 


咱們在第一次作push的時候就會開始執行了動畫,這樣可讓速度更優

.queue(1000) , .queue(2000) , queue(3000)

if (type === "fx" && queue[0] !== "inprogress") {
    //若是隊列沒有被鎖住, 即此時沒有在執行dequeue. 移出隊列裏第一個函數並執行它.  
    jQuery.dequeue(this, type);
}

 

當第一個動畫執行完畢後,那麼必須有一個回調通知這個去把隊列中下一個執行給取出來,而後要刪掉這個佔位,依次循環

opt.complete = function() {
    if (jQuery.isFunction(opt.old)) {
        opt.old.call(this);
    }

    if (opt.queue) {
        jQuery.dequeue(this, opt.queue);
    }
};

 

因此可見,動畫的執行其實最終是依賴queue與dequeue的處理,只是說在執行開始與執行完畢作了一個流程的控制

具體動畫內部怎麼執行的,從下章開始分析

相關文章
相關標籤/搜索