圖片懶加載徹底指導(上)

如今不管是對於網站仍是App應用,圖片都成了相當重要的組成部分,不管是營銷橫幅、商品圖片,仍是logo,一個網站若是連一幅圖都沒有,那對於瀏覽體驗來講是不可想象的。而與此同時,圖片的大小也瓜熟蒂落地成爲每次請求資源中的大頭。根據網站Http Archive當前時間點的統計數據,桌面和移動設備的頁面請求資源的平均總量分別是:1828.2KB1682.4KB,而圖片在其中的所佔據的大小分別爲:899.6KB862.9KB,能夠粗略的計算出圖片的佔比分別是:49%51%。這說明當咱們在考慮網站的性能優化問題時,已經不能無視圖片的存在了。這篇文章咱們將討論圖片懶加載技術,即在保留頁面中全部圖片資源的同時,如何來提高頁面加載速度和減少頁面大小。css

在深刻探討以前,先來看一個圖片懶加載的實例,觀察到在滾動頁面時,顯示在屏幕視窗中的圖片纔會發起請求,等拿到真實圖片後便替換本來的佔位空間。html

1. 圖片的懶加載

圖片懶加載指的是Web及應用開發中相關的一組技術,指經過將圖片的請求加載時間點,推遲到不得不展現時再去請求加載,而非一開始就去請求頁面所包括的所有圖片資源。這將有效地提高性能、改善設備資源的利用率以及減小相關的開銷。前端

其實能夠對頁面上幾乎任何資源採起懶加載技術,好比一個單頁面應用中,若是一個js文件不須要立刻使用,那麼最好就不要在初始化的時候加載它。一樣一張圖片若是不會在首屏中展現,那麼最好的作法就是能要展現的時候,再加載它。web

本文將關注圖片的懶加載以及如何在網站中更優雅的實現。瀏覽器

1.1 懶加載的重要性

理解了圖片懶加載的原理,能夠明顯看出這項功能所帶來的兩個好處:性能優化

1. 性能優化網絡

對於網站管理員來講最重要的事情,莫過於良好的性能與加載時間。採用了懶加載後,便會減小網站頁面初始化時須要加載的圖片數量。較少的資源請求意味着,對於用戶網絡帶寬較少的佔用。這也將確保對剩下資源更快的下載與處理速度,好處顯而易見。app

2.2. 下降成本前端性能

另外一個好處是傳輸成本方面,傳輸圖片或其它任何類型的資源時,一般都是以所傳輸的字節數來計費的。如前所述,懶加載對於不展現的圖片,就不會加載,所以這就下降了頁面傳輸的總字節數。這對於只瀏覽頁面頂部或打開後就關閉的用戶來講,特別節約成本。這一點也將在本文後續的探討中更加明顯。函數

1.2 哪些圖片能夠作懶加載?

圖片懶加載的基本想法很簡單,對於不是立馬須要的資源都延遲加載。對於圖片來講,凡是不在首屏視窗中的圖片均可以作懶加載,而當用戶滾動頁面,圖片的佔位空間出如今視窗後,再觸發圖片資源的請求而後加載。

另外咱們可使用一些工具來識別,哪些圖片應該進行懶加載以減小頁面初始化時的字節傳輸。這裏推薦兩個工具:

  • Lighthouse,Google的前端性能分析工具,生成的報告細緻入微並且頗有指導意義,後面有機會我會單獨寫文章作細緻分析。
  • ImageKit,在線性能分析工具,免費版可主要針對網站所使用的圖片進行分析,並會給出相應的優化建議。

能夠看出懶加載對性能提高和優化用戶體驗都很重要,接下來咱們將詳細的介紹各類實現懶加載的方式。

2. 懶加載的實現

常見的網頁加載圖片有兩種方式:使用<img>標籤;使用CSS的background屬性。

2.1 <img>標籤方式

懶加載能夠被拆解成兩步:

  1. 防止圖片一上來就被加載。瀏覽器會檢查<img />標籤的src屬性來觸發圖片的加載,不管是第一張圖片或是還未出如今屏幕視窗中的第1000張圖片都無所謂,只要瀏覽器在解析HTML後,發現<img>標籤的src屬性上有圖片的URL,就會發起請求加載圖片。因此實現懶加載的第一步,就是將圖片的URL存儲在一個非src屬性上,能夠是data-src,而將src屬性值暫設爲空。 <img data-src="https://xxx/image.jpg" src="" />
  2. 觸發圖片加載。當滾動頁面等操做,使得圖片出如今屏幕視窗中時,觸發圖片的加載。這一步的關鍵是檢測圖片出如今視窗中的時刻,接下來介紹兩種實現方式。

JavaScript事件方式

這裏將使用到三個檢測瀏覽器視窗改變的事件:scrollresizeorientationChange

  • scroll:用戶滾動頁面時觸發
  • resize:當瀏覽器窗口尺寸改變時觸發
  • orientationChange:通常針對移動端,當設備在橫屏與豎屏模式下切換時觸發

這些改變的時刻,都將有可能使以前一些未出如今瀏覽器視窗中的圖片如今出如今了視窗中。當這些事件觸發時,經過檢查每一個不曾加載圖片位置信息,來判斷其當前是否出如今視窗中,判斷方法以下圖所示: evernotecid://97E3A084-2194-4244-AC3A-BA8AC29EED05/appyinxiangcom/7428242/ENResource/p341

當圖片出如今視窗上後,便將其 data-src的屬性賦值給 src屬性,來觸發圖片的加載,同時移除標識爲懶加載的類名 lazy,防止後續事件再次觸發後重復加載。最後當全部待懶加載的圖片都加載完畢後,移除事件監聽。另外,容易看出 scroll事件會被屢次重複觸發,出於性能考慮,能夠設置一小段延遲,來限制回調函數的即時執行。

document.addEventListener("DOMContentLoaded", function() {
  var lazyloadImages = document.querySelectorAll("img.lazy");    
  var lazyloadThrottleTimeout;
  
  function lazyload () {
    if(lazyloadThrottleTimeout) {
      clearTimeout(lazyloadThrottleTimeout);
    }    
    
    lazyloadThrottleTimeout = setTimeout(function() {
        var scrollTop = window.pageYOffset;
        // 遍歷檢查全部懶加載圖片
        lazyloadImages.forEach(function(img) {
            if(img.offsetTop < (window.innerHeight + scrollTop)) {
              img.src = img.dataset.src;
              img.classList.remove('lazy');
            }
        });
        // 無未加載圖片時,移除相關事件監聽
        if(lazyloadImages.length == 0) { 
          document.removeEventListener("scroll", lazyload);
          window.removeEventListener("resize", lazyload);
          window.removeEventListener("orientationChange", lazyload);
        }
    }, 20);
  }
  // 添加事件監聽
  document.addEventListener("scroll", lazyload);
  window.addEventListener("resize", lazyload);
  window.addEventListener("orientationChange", lazyload);
});
複製代碼

對於首頁就在視窗中展現的圖片,無需經過事件觸發才進行加載處理,出於用戶體驗考慮,直接加載就好。

Intersection Observer API方式

Intersection Observer API是一個相對較新的API,在此以前,爲了判斷一個元素是否出如今視窗,並作響應的處理操做,咱們只能像上一節介紹的那樣:綁定事件後在處理函數中,經過相關位置屬性值的計算,來肯定元素是否出如今視窗。姑且先不考慮這種方式的性能表現,單就計算的繁瑣就算不上優雅的實現。

下面使用Intersection Observer API來實現懶加載:經過將觀察者對象綁定到全部待加載的圖片上後,當圖片元素出如今視窗上觸發監聽,判斷isIntersecting屬性來將<img>標籤data-src上的URL賦值給src屬性以加載圖片。具體代碼以下:

document.addEventListener("DOMContentLoaded", function() {
  var lazyloadImages;    
  // 瀏覽性兼容性判斷
  if ("IntersectionObserver" in window) {
    lazyloadImages = document.querySelectorAll(".lazy");
    var imageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        // 圖片是否進入視窗
        if (entry.isIntersecting) {
          var image = entry.target;
          image.src = image.dataset.src;
          image.classList.remove("lazy");
          imageObserver.unobserve(image);
        }
      });
    });
    // 將觀察者註冊到全部圖片上
    lazyloadImages.forEach(function(image) {
      imageObserver.observe(image);
    });
  } else {  
    // 對於不兼容intersection observer API的瀏覽器使用事件綁定方式
    ...
  }
})
複製代碼

若是咱們來比較這兩種實現懶加載的方式,顯然使用Intersection Observer API的方式對於圖片加載的觸發時刻會比事件監聽的方式更快。同時如同全部新方法同樣,優良的表現背後都隱藏着兼容性的劣勢,對於不支持此API的瀏覽器咱們在使用的時候,還需注意兼容處理。 evernotecid://97E3A084-2194-4244-AC3A-BA8AC29EED05/appyinxiangcom/7428242/ENResource/p342

2.2 使用CSS的Background方式

CSS的background在頁面中顯示圖片的方式,和上面<img>的方式相比可能不是那麼直觀。使用這種加載圖片的方式,瀏覽器須要先構建DOM樹和CSSOM樹後,以肯定當前文檔的DOM節點上CSS的樣式。若是指定了background-image的CSS規則沒有應用在文檔的元素上,瀏覽器就不會加載相應的圖片,反之只有background-image的CSS規則應用於文檔某元素上時,瀏覽器纔會請求加載對應圖片。這一性質也就成了該方式實現懶加載的原理。以下HTML中的標籤將不只限於<img>

<div id="bg-image" class="lazy"></div>
複製代碼

CSS屬性設置以下

#bg-image.lazy {
   background-image: none;
   background-color: #F1F1FA;
}
#bg-image {
  background-image: url("https://xxx/image10.jpeg");
  max-width: 600px;
  height: 400px;
}
複製代碼

初始化狀況下,background-image屬性爲none不加載圖片。判斷圖片加載時刻所用的方法,沿用上一節intersection observer API或事件監聽均可以,當須要加載時,移除元素類名lazy後,background-image非空的CSS規則就會覆蓋以前的規則,從而發起對圖片資源的請求並加載之。

相關文章
相關標籤/搜索