前端性能優化--惰性加載

圖片描述

從需求出發:

在實際的項目開發中,我遇到了一個這樣的需求:一個頁面模塊有不少列表數據展現,每條數據都帶有圖片,而首次展現的圖片只須要不到10張,那麼咱們還要一次性把全部圖片都加載出來嗎?顯然這是不對的,不只影響頁面渲染速度,還浪費帶寬(由於須要對列表進行拖動排序,需加載出所有列表,不能作分頁)。咱們能夠在瀏覽器滾動到必定的位置的時候進行下載,這也就是們一般所說的惰性加載,技術上現實其中要用的技術就是圖片懶加載--到可視區域再加載。
圖片描述css

實現方案:

一、默認不加載圖片,只加載佔位符
二、組件滾動條變化
三、計算可視區域,觸發條件
四、<img>標籤src屬性加載資源前端

知識點:

scrollTop:外框元素的滾動高度
offsetTop:元素相對於最近的包含該元素的定位元素(具備position屬性且不是static)邊框的距離。若是沒有定位的元素,則默認body。
offsetHeight:它返回該元素的像素高度,高度包含該元素的垂直內邊距和邊框,且是一個整數。
計算:可視區域的高度(offsetHeight) + 滾動條捲去的高度(scrollTop) >=  元素相對於外框的距離(offsetTop) - 偏移量 (提早加載)
圖片描述segmentfault

代碼實現:

頁面結構api

<style type="text/css">
.container{
    width:200px;
    height:200px;
    position:relative;
    overflow-y:scroll;
}
.img-area{
    width:100px;
    height:100px;
}
</style>
<div class="container">
    <div class="img-area">
        <img class="pic" alt="loading" data-src="./img/img1.png" src="image-placeholder-logo.svg">
    </div>
    <div class="img-area">
        <img class="pic" alt="loading" data-src="./img/img2.png" src="image-placeholder-logo.svg">
    </div>
    <div class="img-area">
        <img class="pic" alt="loading" data-src="./img/img3.png" src="image-placeholder-logo.svg">
    </div>
    <div class="img-area">
        <img class="pic" alt="loading" data-src="./img/img4.png" src="image-placeholder-logo.svg">
    </div>
    <div class="img-area">
        <img class="pic" alt="loading" data-src="./img/img5.png" src="image-placeholder-logo.svg">
    </div>
</div>

src屬性統一用一個佔位圖片,alt屬性是在圖像沒法顯示時的替代文本。 data-src是自定義屬性,用來保存實際的圖片地址,能夠經過HTMLElement.dataset來訪問。
腳本代碼:數組

var container = document.querySelector('.container');
    container.onscroll = function(){
        checkImgs();
    }
    function isInSight(el) {
        var sTop = container.scrollTop;
        var oHeight = container.offsetHeight;
        var oTop = el.offsetTop;
        return sTop + oHeight > oTop;
    }
    function checkImgs() {
      var imgs = document.querySelectorAll('.pic');
      Array.from(imgs).forEach(el => {
        if (isInSight(el)) {
          loadImg(el);
        }
      })
    }
    function loadImg(el) {
        var source = el.dataset.src;
        el.src = source;
    }
        checkImgs();

能夠看出,頁面加載時候,綁定外框的scroll事件,隨着用戶向下滾動鼠標,把img的src賦予新的值,網絡從新發起請求,拉取圖片。這裏應該是有一些能夠優化的地方,好比
一、能夠只監聽向下滾動時候的事件,並設置延時(使用截流函數),防制屢次調用回調函數。
二、能夠設一個標識符標識已經加載圖片的index,當滾動條滾動時就不須要遍歷全部的圖片,只須要遍歷未加載的圖片便可。
三、能夠在計算的時候,增長偏移數據,提早加載圖片,並使用淡入效果,提升流暢性。瀏覽器

另外一種計算方法:

getClientRects()方法返回的一組矩形的集合, 即:是與該元素相關的CSS 邊框集合 。包含邊框的只讀屬性left、top、right和bottom,單位爲像素。除了 width 和 height 外的屬性都是相對於視口的左上角位置而言的。
圖片描述網絡

這種條件下,假設bound = el.getBoundingClientRect(),隨着滾動條的向下滾動,bound.top會愈來愈小,也就是圖片到可視區域頂部的距離愈來愈小,當bound.top===clientHeight時,圖片的上沿應該是位於可視區域下沿的位置的臨界點,再滾動一點點,圖片就會進入可視區域。
也就是說,在bound.top<=clientHeight時,圖片是在可視區域內的。前端性能

function isInSight(el) {
    var bound = el.getBoundingClientRect();
    var clientHeight = window.innerHeight;
    return bound.top <= clientHeight;
}

 進一步考慮:

以上監聽scroll,並計算元素位置來實現惰性加載。當數據達到必定量的時候,事件綁定和循環位置計算會消耗大量的性能,每次調用 getBoundingClientRect() 都會強制瀏覽器 從新計算整個頁面的佈局 ,可能給你的網站形成至關大的閃爍。這種方式有些美中不足。svg

交叉觀察器:

IntersectionObserver 就是爲此而生的,它是HTML5新增的api,能夠檢測一個元素是否可見,IntersectionObserver 能讓你知道一個被觀測的元素何時進入或離開瀏覽器的視口。
圖片描述函數

它兼容性有限,
Chrome 51+(發佈於 2016-05-25)
Android 5+ (Chrome 56 發佈於 2017-02-06)
Edge 15 (2017-04-11)
iOS 不支持

不過不用擔憂,WICG 提供了一個polyfill,能夠兼容到如下版本:
圖片描述

它的用法也很簡單,相似於rxjs中的observe。

var observe = new IntersectionObserver(callback, option);

 IntersectionObserver是瀏覽器原生提供的構造函數,接受兩個參數:callback是可見性變化時的回調函數,option是配置對象(可選)。返回一個觀測實例observe,能夠指定觀測哪一個DOM節點。

// 開始觀察
observe.observe(document.getElementById('example'));
callback = function(entries){
    entries.forEach((entry) => {
        if (entry.isIntersecting) {
        //開始進入,交叉狀態,在此處理圖片邏輯。
        } else {
        //已徹底進入或徹底離開
        }
    });
}
// 中止觀察
observe.unobserve(element);

// 關閉觀察器
observe.disconnect();

 entries是一個數組,每一個成員都是一個IntersectionObserverEntry對象。舉例來講,若是同時有兩個被觀察的對象的可見性發生變化,entries數組就會有兩個成員。isIntersecting,返回一個布爾值, 若是目標元素與交叉區域觀察者對象的根相交,則返回 true 。若是返回 true,則描述了變換到交叉時的狀態;若是返回 false, 那麼能夠由此判斷,變換是從交叉狀態到非交叉狀態。

IntersectionObserverEntry對象提供了不少有用的屬性,好比target是被觀察的目標元素,是一個 DOM 節點對象,intersectionRatio是目標元素的可見比例,即DOM節點的可見面積和總面積的比例,徹底可見時爲1,徹底不可見時小於等於0,能夠經過此屬性設置圖片的透明度,作成淡出的效果。

下拉無限滾動:

圖片描述

在頁面底部有一個loading狀態標籤。一旦標籤可見,就表示用戶到達了頁面底部,從而加載新的條目放在標籤的前面。這樣作的好處是,比監聽scroll和計算節省了不少性能消耗,現有IntersectionObserver能夠很簡單的應用。下面是實現方法:

var intersectionObserver = new IntersectionObserver(
    function (entries) {
    // 若是不可見,就返回
    if (entries[0].intersectionRatio <= 0) return;
    //在此加載新的數據
});
intersectionObserver.observe(document.getElementById('loading'));

 小結:

圖片(不僅有圖片,主要是圖片佔用的資源最多最多見)惰性加載是一種網頁優化技術。經過多種方案對比,使圖片僅在瀏覽器當前視窗內出現時才加載該圖片,達到減小首屏圖片請求數,優化前端性能,提升用戶體驗。無論哪一種方法,都有其本身的優點和劣勢,掌握其中的原理,靈活應用纔是最重要的。這對開發中遇到的問題及解決方法進行了總結,都是實戰得來的經驗,描述不清或者不對的地方,請多多指教。

相關文章
相關標籤/搜索