javascript圖片懶載入與預載入的分析

龍恩0707發表於2014-01-19

懶載入與預載入的基本概念。

  懶載入也叫延遲載入:前一篇文章有介紹:JS圖片延遲載入 延遲載入圖片或符合某些條件時才載入某些圖片。

   預載入:提前載入圖片,當使用者需要檢視時可直接從本地快取中渲染。

 兩種技術的本質:兩者的行為是相反的,一個是提前載入,一個是遲緩甚至不載入。懶載入對伺服器前端有一定的緩解壓力作用,預載入則會增加伺服器前端壓力。

 懶載入的意義及實現方式有:

   意義: 懶載入的主要目的是作為伺服器前端的優化,減少請求數或延遲請求數。

   實現方式: 

      1.第一種是純粹的延遲載入,使用setTimeOut或setInterval進行載入延遲.

    2.第二種是條件載入,符合某些條件,或觸發了某些事件才開始非同步下載。

    3.第三種是可視區載入,即僅載入使用者可以看到的區域,這個主要由監控滾動條來實現,一般會在距使用者看到某圖片前一定距離遍開始載入,這樣能保證使用者拉下時正好能看到圖片

  預載入的意義及實現方式有:

    預載入可以說是犧牲伺服器前端效能,換取更好的使用者體驗,這樣可以使使用者的操作得到最快的反映。實現預載的方法非常多,可以用CSS(background)、JS(Image)、HTML(<img />)都可以。常用的是new Image();,設定其src來實現預載,再使用onload方法回撥預載完成事件。只要瀏覽器把圖片下載到本地,同樣的src就會使用快取,這是最基本也是最實用的預載方法。當Image下載完圖片頭後,會得到寬和高,因此可以在預載前得到圖片的大小(方法是用記時器輪循寬高變化)。

怎麼樣才能實現預載入?

 我們可以通過google一搜尋:可以看到很多人用這種方式進行預載入:程式碼如下:

function loadImage(url,callback) {
    var img = new Image();
    
    img.src = url;
    img.onload = function(){
        img.onload = null;
        callback.call(img);
    }
}

在google或者火狐下測試 都是正常的 不管我怎麼重新整理都是正常的,但是在IE6下不是這樣的 我點選一下 是正常 再次點選或者重新重新整理都不正常。下面的jsfiddle地址:有興趣的同學可以試試 點選按鈕後 彈出正常結果 再次點選在IE6下就不執行onload裡面的方法了,接著重新重新整理也不行。

 想要看效果,點選我!

為什麼其他瀏覽器正常的:其實原因很簡單,就是瀏覽器快取了,除了IE6以外(即說opera也會,但是我特意用opera試了下,沒有,可能版本的問題吧,或許現在已經修復了。),其他瀏覽器重新點選會再次執行onload方法,但是IE6是直接從瀏覽器取的。

那現在怎麼辦?最好的情況是Image可以有一個狀態值表明它是否已經載入成功了。從快取載入的時候,因為不需要等待,這個狀態值就直接是表明已經下載了,而從http請求載入時,因為需要等待下載,這個值顯示為未完成。這樣的話,就可以搞定了。經過google搜尋下即介紹:發現有一個為各個瀏覽器所相容的Image的屬性——complete。所以,在圖片onload事件之前先對這個值做一下判斷即可。最後,程式碼變成如下的樣子:

function loadImage(url,callback) {
    var img = new Image();
    
    img.src = url;

    if(img.complete) {  // 如果圖片已經存在於瀏覽器快取,直接呼叫回撥函式
        
        callback.call(img);
        return; // 直接返回,不用再處理onload事件
    }

    img.onload = function(){
        img.onload = null;
        callback.call(img);
    }
}

也就是說如果圖片已經在瀏覽器快取裡面 那麼支援直接從瀏覽器快取取得直接執行img.complete裡面的函式 接著返回.

但是我們可以看到上面的程式碼:必須等圖片載入完成後,可以執行回撥函式,也可以說等圖片載入後,我們可以獲取圖片的寬度和高度。那麼如果我們想提前獲取圖片的尺寸那怎麼辦?上網經驗告訴我:瀏覽器在載入圖片的時候你會看到圖片會先佔用一塊地然後才慢慢載入完畢,並且不需要預設width與height屬性,因為瀏覽器能夠獲取圖片的頭部資料。基於此,只需要使用javascript定時偵測圖片的尺寸狀態便可得知圖片尺寸就緒的狀態。程式碼如下:(但是有個前提是 這個方式不是我想的,也不是我寫的程式碼,是網上朋友總結的程式碼 我只是知道有這麼一個原理)

var imgReady = (function(){
    var list = [],
        intervalId = null;

    // 用來執行佇列
    var queue = function(){

        for(var i = 0; i < list.length; i++){
            list[i].end ? list.splice(i--,1) : list[i]();
        }
        !list.length && stop();
    };
    
    // 停止所有定時器佇列
    var stop = function(){
        clearInterval(intervalId);
        intervalId = null;
    }
    return function(url, ready, error) {
        var onready = {}, 
            width, 
            height, 
            newWidth, 
            newHeight,
            img = new Image();
        img.src = url;

        // 如果圖片被快取,則直接返回快取資料
        if(img.complete) {
            ready.call(img);
            return;
        }
        width = img.width;
        height = img.height;

        // 載入錯誤後的事件 
        img.onerror = function () {
            error && error.call(img);
            onready.end = true;
            img = img.onload = img.onerror = null;
        };

        // 圖片尺寸就緒
        var onready = function() {
            newWidth = img.width;
            newHeight = img.height;
            if (newWidth !== width || newHeight !== height ||
                // 如果圖片已經在其他地方載入可使用面積檢測
                newWidth * newHeight > 1024
            ) {
                ready.call(img);
                onready.end = true;
            };
        };
        onready();
        // 完全載入完畢的事件
        img.onload = function () {
            // onload在定時器時間差範圍內可能比onready快
            // 這裡進行檢查並保證onready優先執行
            !onready.end && onready();
            // IE gif動畫會迴圈執行onload,置空onload即可
            img = img.onload = img.onerror = null;
        };
        
        
        // 加入佇列中定期執行
        if (!onready.end) {
            list.push(onready);
            // 無論何時只允許出現一個定時器,減少瀏覽器效能損耗
            if (intervalId === null) {
                intervalId = setInterval(queue, 40);
            };
        };
    }
})();

呼叫方式如下:

imgReady('http://img01.taobaocdn.com/imgextra/i1/397746073/T2BDE8Xb0bXXXXXXXX-397746073.jpg',function(){
  alert('width:' + this.width + 'height:' + this.height);
});

相關文章