1. 程式人生 > >web 前端圖片懶載入實現原理

web 前端圖片懶載入實現原理

前端時間面試的時候老是被問到圖片懶載入實現及原理,由於自己在實際專案中並沒有用過,只是瞭解過大概,所以回答起來都不盡如人意,趁這段時間空閒下來有時間好好研究下,話不多說,直奔主題~

一、html(這裡只列出相關的結構,body那些就不列了~)

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
<img class="imgLazyLoad" data-src="http://office.qq.com/images/title.jpg" />
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

其中,img標籤就是我們今天的主角,data-src屬性上面儲存著我們後面需要動態載入的圖片地址,初始化時圖片沒有設定任何連結~

二、js(css就略過了)

(function(){//立即執行函式
    let imgList = [],delay,time = 250,offset = 0;
    function _delay(){//函式節流
        clearTimeout(delay);
        delay = setTimeout(() => {
            _loadImg();
        },time)
    };
    function _loadImg(){//執行圖片載入
        for(let i = 0,len = imgList.length; i < len; i++){
            if(_isShow(imgList[i])){
                imgList[i].src = imgList[i].getAttribute('data-src');
                imgList.splice(i,1);
            }
        }
    };
    function _isShow(el){//判斷img是否出現在可視視窗
        let coords = el.getBoundingClientRect();
        return (coords.left >= 0 && coords.left >= 0 && coords.top) <= (document.documentElement.clientHeight || window.innerHeight) + parseInt(offset);
    };
    function imgLoad(selector){//獲取所有需要實現懶載入圖片物件引用並設定window監聽事件scroll
        _selector = selector || '.imgLazyLoad';
        let nodes = document.querySelectorAll(selector);
        imgList = Array.apply(null,nodes);
        window.addEventListener('scroll',_delay,false)
    };
    imgLoad('.imgLazyLoad')
})()

我們來一段段解釋~

1.首先我們把這段js封裝到自執行函式裡面,也就是:

(function(){})()

這型別的函式,目的是生成一個新的執行上下文環境,防止裡面的變數汙染全域性環境。 
想學習更多相關知識,請點選js立即執行函式

2.宣告所需引數:

var imgList = [],delay,time,offset;
  • imgList:儲存所有圖片節點的陣列
  • delay:儲存的是setTimeout生成的引用
  • time:控制節流函式延遲執行的時間
  • offset:設定圖片距離可視區域多遠則立即載入的偏差值

3.監聽scroll事件,執行節流函式

function imgLoad(selector){
    _selector = selector || '.imgLazyLoad';
    let nodes = document.querySelectorAll(selector);
    imgList = Array.apply(null,nodes);
    window.addEventListener('scroll',_delay,false)
};
  • 獲得圖片列表:使用document.querySelectorAll方法獲取所有需要實現懶載入的圖片列表。 
    這裡得到的只是個nodeList,所以這裡利用Array.apply將nodes轉變成一個數組儲存到imgList中。 
    更多apply相關知識點請點選apply用法
  • window監聽事件:window監聽scroll事件,執行節流函式_delay,此函式後面介紹

4.宣告節流函式

function _delay(){//函式節流
    clearTimeout(delay);
    delay = setTimeout(() => {
       _loadImg();
    },time)
};
  • 下劃線函式:加下劃線函式命名沒有特殊功能,只是一種約定成俗的做法,表明這是一個私有函式(並沒有強制規定使用,看團隊習慣斟酌使用)
  • 函式節流目的:在類似scroll、resize事件中執行大量DOM操作或者計算時,就會出現再次觸發事件而上一次事件中的DOM操作和計算還沒完成的情況,結果瀏覽器掉幀了,導致效能下降,影響使用者體驗。 
    想了解更多相關知識點,請點選瀏覽器瀏覽器scroll優化
  • 函式節流原理:每次執行_delay函式先清除上一次setTimeout生成的引用,阻止上一次的函式呼叫(如果還沒執行的話),然後建立一個新的setTimeout,在time儲存的時間間隔後呼叫函式

5.載入圖片

function _loadImg(){//執行圖片載入
    for(let i = 0,len =imgList.length; i < len; i++){
        if(_isShow(imgList[i])){
            imgList[i].src = imgList[i].getAttribute('data-src');
            imgList.splice(i,1);
        }
    }
};
  • 何時顯示判斷:迴圈輸出每個imgList中儲存的圖片物件,_isShow函式判斷是否需要顯示圖片,需要的話,立即從圖片物件中的data-src屬性中取得連結並賦值給當前圖片的src進行載入
  • 圖片物件刪除:每次判斷圖片需要顯示並進行載入後從陣列中取出此圖片物件引用,避免下次迴圈重複判斷

6.判斷圖片是否顯示

function _isShow(el){
    let coords = el.getBoundingClientRect();
    return (coords.left >= 0 && coords.left >= 0 && coords.top) <=(document.documentElement.clientHeight || window.innerHeight) + parseInt(offset);
};
  • 何時顯示圖片? 
    當圖片出現在螢幕可視局域時顯示~
  • 怎麼判斷圖片出現在圖片可視區域? 通過呼叫元素的getBoundingClientRect方法獲得一個包含了一組用於描述邊框的相對於視口的左上角位置而言的只讀屬性——left、top、right和bottom,在和瀏覽器視口相應寬高進行對比即可判斷元素是否出現在可視區域中,offset是偏差值,可以進行顯示偏差設定

    1.top:此元素上邊框距離瀏覽器視口頂部大小
    2.left:此元素左邊框距離瀏覽器視口左邊大小
    3.right:此元素右邊框距離瀏覽器視口左邊大小
    4.bottom:此元素底邊框距離瀏覽器視口頂部大小
    

    注意這裡right和bottom的描述,不是距離視口右邊和底部,而是左邊和頂部~此例子中只考慮垂直方向上的值(top)和瀏覽器視口高度的比較。 
    檢視更多元素的getBoundingClientRect方法知識,請點選元素getBoundingClientRect方法

綜上,圖片懶載入功能初步形成,當然還有很多可以優化的地方(比如只有使用者停止滾動圖片才會顯示),後續會不斷完善~