頁面圖片預載入與懶載入策略

程式碼星空發表於2019-01-14

在圖片的載入策略之前,我們先來了解下html網頁中,圖片的不同位置的圖片分別是在什麼時候發起圖片資源請求的

img 標籤

img標籤會在html渲染解析到的時候,如果解析到img src值,則瀏覽器會立即開啟一個執行緒去請求該資源。 正常情況是解析到了src便發起請求,

  1. img 標籤隱藏 通過css樣式隱藏img的顯示
<img src="img1.jpg" style="display: none" />  
複製程式碼

發現除了Opera不請求,其他瀏覽器都會馬上請求,

<img src="img1.jpg" style="visible: hidden" /> 
複製程式碼

全部瀏覽器都會請求 2. img 同一張圖重複

<img src="img2.jpg" />  
<img src="img2.jpg" /> 
複製程式碼

所有的瀏覽器都只請求一次,因為http在發出請求的時候,會檢驗是否有快取,有快取就會從快取讀取 那麼你知道如何判斷資源是否是快取還是伺服器返回的呢,看這邊

img 在background中

  1. 重複背景
<style type="text/css">   
    .test1 { background: url(bg1.jpg) }   
    .test2 { background: url(bg1.jpg) }   
</style>   
<div class="test1">test1</div>   
<div class="test2">test2</div>  
複製程式碼

所有瀏覽器都只發起一次請求

  1. 隱藏元素背景
<style type="text/css">   
    .test3 { display: none; background: url(bg2.jpg) }   
</style>   
<div class="test3">test1</div>  
複製程式碼

Opera 和Firefox對display:none的元素的背景,不會立即發生請求,只有當其display 不為none才會發起圖片請求。其他瀏覽器則是立即發起請求

  1. 重寫背景
<style type="text/css">   
    .test1 { background: url(bg1.jpg) }   
    .test1 { background: url(bg2.jpg) }   
</style>   
<div class="test1">test1</div>  
複製程式碼

重寫背景,瀏覽器只會請求覆蓋的那個背景圖

  1. 多重背景
<style type="text/css">   
    .test1 { background-image:url("haorooms.jpg"),url("http2.jpg"); }   
</style>   
<div class="test1">test1</div>
複製程式碼

對全部的背景都會請求

  1. 元素不存在,但是設定了背景
<style type="text/css">   
    .test3 { background: url(bg3.jpg) }   
    .test4 { background: url(bg4.jpg) }   
</style>   
<div class="test3">test1</div>   
複製程式碼

.test4並不存在,這個時候,瀏覽器並不會去請求bg3.jpg,當且僅當背景的應用元素存在時(不管在當前是顯示還是不顯示),才會發生請求

  1. hover背景
<style type="text/css">   
    a.test1 { background: url(haorooms.jpg); }   
    a.test1:hover { background: url(http2.jpg); }   
</style>   
<div href="#" class="test1">test1</div>
複製程式碼

觸發hover的時候,才會請求hover下的背景。在實際中,會遇到這個背景圖初次顯示閃一下的情況,如果要優化,就預載入這張圖即可。

  1. js動態生成img並賦值
<script type="text/javascript">   
    var el = document.createElement('div');   
    el.innerHTML = '<img src="haorooms.jpg" />';   
    //document.body.appendChild(el);   
</script> 
複製程式碼

只有Opera 不會馬上請求圖片,其他瀏覽器都是執行了程式碼就發起請求,Opera一定要元素新增到dom時,才會發出請求

在做專案的過程中,經常會遇到需要圖片預載入與懶載入,圖片預載入就是為了在展示的時候減少圖片載入過程不好的載入體驗,而圖片懶載入則是處於這張圖片不在當前可視區域展示,為了網路頻寬以及提升首次載入速度而做的優化。

圖片預載入

  • 1.img標籤 如上面所列舉的那些情況,就可以利用,比如用img標籤與dom的background-image 來達到預載入的效果。在展示前就可以保證圖片資源已經載入完成。
    1. js Image物件
    const preloadImg = (url) => {
        const img = new Image();
        if(img.complete) {
            //圖片已經載入過了,可以使用圖片
            //do something here
        }
        else {
            img.onload = function() {
                //圖片首次載入完成,可以使用圖片
                //do something here
            };
        }
        img.src = url;
    }
複製程式碼

注意,最好是先定義onload,再賦值src,不然會出現資源返回,但是onload還沒有掛載的情況。
在實際的專案我遇到過的,就是需要在某些圖片載入完成再做下一步,那麼這個時候,我們就需要知道某些圖片序列確定是預載入完成,同樣還是使用preloadImg,結合一下promise,有多個圖片資源,可以用promise.all。就可以保證所有的圖片載入完成再進行下一步

var promiseAll = imgData.map(function (item, index) {
    return new Promise(function (resolve, reject) {
      var img = new Image();
      img.onload = function () {
        img.onload = null;
        resolve(img);
      };
      img.error = function () {
        reject('圖片載入失敗');
      };
      img.src = item;
    });
  });
  Promise.all(promiseAll).then(
    function () {
      // 圖片全部載入完成,進行下一步
      // todo
    },
    function (err) {
      console.log(err);
    }
  );
複製程式碼

圖片懶載入

所謂圖片懶載入,就是延遲載入圖片資源,是對網頁效能的一種優化方式,比如當我們開啟一個網頁的時候,優先展示的圖片,比如首屏圖片,就先載入,而其他的圖片,當需要展示的時候,再去請求圖片資源,避免了首次開啟時,一次性載入過多圖片資源。

1.最簡單的圖片懶載入

同樣地,我們再回顧一下文章開始講的,如果是img標籤,瀏覽器解析到img的src有值,就會去發起請求,那麼我們就可以藉助這個操作,在懶載入的圖片,先不賦值,等到需要展示的時候,再賦值

<img class="show-img" data-src="//static/show.jpg" />

const src = $('.show-img').attr('data-src');
$('.show-img').attr(src, src);

複製程式碼

在距離可視區域一定距離的時候載入圖片

一般實際中,我們不會在需要展示的時候,才發起圖片請求,不然就不會有圖片預載入的需求了,那麼如何判斷圖片在何時需要展示呢?在下拉流之類的網頁中,我們一般是在圖片距離可視區域的一定距離,比如50px,就開始請求圖片資源

如何判斷圖片是否在可視區域中?

  • 原生js判斷元素是否在可視區域內,這裡的可視區域的距離都是以垂直距離為準

方法一:
A: document.documentElement.clientHeight 可視視窗的高度
B: element.offsetTop dom相對於文件頂部的距離
C: document.documentElement.scrollTop 滾動條滾動的距離
B - C < A 即說明元素在可視區域內

方法二:getBoundingClientRect
const domObj = element.getBoundingClientRect(); domObj.top:元素上邊到視窗上邊的距離;
 domObj.right:元素右邊到視窗左邊的距離;
 domObj.bottom:元素下邊到視窗上邊的距離;
 domObj.left:元素左邊到視窗左邊的距離;
const clientHeight = window.innerHeight;
當 domObj.top < clientHeight 表示dom在可視區域內了

實際應用

const preImages = $('img[data-src]').not('.pred-img');
Array.from(preImages).forEach((item) => {
    if (isPreLoad(item)) {
        loadImg(item);
    }
});

const loadImg = (img) => {
    if (!img.src) {
        img.src = img.attr('data-src').addClass('pred-img');
    }
};

const isPreLoad = () {
    const preObj = getBoundingClientRect();
    const cH = $(window).height();
    return preObj <= cH + 100;
};
複製程式碼

程式碼中有二處,一處是not('.pred-img'),作為載入過的圖片的標記,第二處是識別區域高度加了100,提前100px的地方就開始載入。當然在具體展示的時候,還可以給圖片新增載入中的樣式,以及識別圖片載入異常

 <img class="avater scrollLoading"  data-src="../image/show.png" onerror='this.src="../images/avatar.png"'>
複製程式碼

_ ### jquery.lazyload 外掛

<div class="article-content">
    <img data-original="img/show1.jpg">
    <img data-original="img/show2.jpg">
    <img data-original="img/show3.jpg">
    <img data-original="img/show4.jpg">
    <img data-original="img/show5.jpg">
    <img data-original="img/show6.jpg">
</div>

require('./libs/jquery.lazyload');
addImagesLazyload();
function addImagesLazyload() {
  var $images = $('.article-content img:not([data-lazyload])');
  var preCount = 2;
  $images.each(function(i, img) {
    var $img = $(img);
    var $box = $img.parent();
    var src = $img.attr('data-original');

    if (src && !src.match(/\?/)) {
      src += '?imageView2/2/w/750';
    }

    $img.attr('data-lazyload', 1);
    $img.off('error').on('error', function() {
      $box.addClass('img-error');
    });
    $img.off('load').on('load', function() {
      $box.removeClass('img-box img-error');
    });

    if (src) {
      $img.attr('data-original', src);
      if (i < preCount) {
        img.setAttribute('src', src);
      }
    }
  });

  var $lazyed = $images.slice(preCount);
  if ($lazyed.length) {
    $lazyed.lazyload({ // 使用lazyload
      threshold: 200 // 圖片距離可視區200px的時候開始請求
    });
  }
}
複製程式碼

preCount 是載入頁面的時候想要載入的圖片,其他的圖片就是懶載入。load 與error中就可以新增圖片載入與載入失敗預設顯示圖片 關於lazyload的配置引數如下:
placeholder : "img/img.jpg"
佔點陣圖片,此圖片用來佔據將要載入的圖片的位置,待圖片載入時,佔點陣圖則會隱藏
effect: "fadeIn"
圖片載入效果, 可取值有show(直接顯示),fadeIn(淡入),slideDown(下拉)
threshold: 200
滾動條在離目標位置還有200的高度時就開始載入圖片,可以做到不讓使用者察覺
event: 'click'
點選事件觸發時才載入
值有click(點選),mouseover(滑鼠劃過),sporty(運動的),foobar(…).可以實現滑鼠莫過或點選圖片才開始載入
ailurelimit : 10
failurelimit,值為數字.lazyload預設在找到第一張不在可見區域裡的圖片時則不再繼續載入,但當HTML容器混亂的時候可能出現可見區域內圖片並沒載入出來的情況,failurelimit意在載入N張可見區域外的圖片,以避免出現這個問題。

相關文章