圖片懶加載幾個版本的實現對比

背景

圖片懶加載是針對圖片加載時機的一種優化,在一些圖片量比較大的網站(比如電商網站首頁,或者團購網站、小遊戲首頁等),如果我們嘗試在用戶打開頁面的時候,就把所有的圖片資源加載完畢,那麼很可能會造成白屏、卡頓等現象,因爲圖片真的太多了,一口氣處理這麼多任務,瀏覽器做不到啊!

懶加載是爲了讓瀏覽器只加載可視區內的圖片,可視區外的大量圖片不進行加載,當頁面滾動到後面去的時候再進行加載。這樣做有很多好處可以增加首屏加載的速度,畢竟,用戶點開頁面的瞬間,呈現給他的只是首屏,我們只要把首屏的資源圖片加載處理就可以了,至於下面的圖片,當用戶下滑噹噹前位置的時候,在加載出來也是沒問題的,對於性能壓力也小了,用戶體驗也沒有變差。

解答

圖片懶加載的原理就是需要知道圖片是否在可視區內了,當圖片到達可視區內就需要去請求對應的圖片加載出來

頁面中的img標籤一般如下寫

<img class="lazyload" src="placeholder.jpg" data-src="real_image.jpg" />

其中 src 首先賦值一個佔位的圖片,一般是一個很小的圖片,進行佔位,data-src 是實際需要展示的圖片,原理就是當圖片在可視區內的時候將 data-src 的圖片選渲染出來即可。

1、原生實現

Chrome 76 將原生支持圖片的惰性加載,支持對 img 和 iframe 進行延遲加載,只需要將 loading 屬性設置爲 lazy 即可。

<img src="celebration.jpg" loading="lazy" alt="..." />
<iframe src="video-player.html" loading="lazy"></iframe>

原生實現的好處是,不需要任何腳本,純原生 HTML 即可,簡單方便,支持多種屬性

原生的壞處就是在於瀏覽器的支持率不是很高,將來肯定是非常好的。

我們知道由於瀏覽器的支持率不是很好,上面的方案固然很好,但是使用的並不是很多,所以下面介紹幾種更加常見的懶加載方案。

2、Element.getBoundingClientRect()

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

有了這個 API 後我們很同意獲取圖片的 top 值,當 top 值小於可視區的高度的時候就可以任何圖片進入了可視區,直接加載圖片即可

element.getBoundingClientRect().top < document.documentElement.clientHeight

由於需要在滾動的時候去監聽圖片的位置,所以我們需要使用到window.onscroll事件,我們在事件內部處理相關的邏輯即可。

3、通過相對計算獲取元素位置

  1. 通過 document.documentElement.clientHeight 獲取屏幕可視窗口高度。

  2. 通過 element.offsetTop 獲取元素相對於文檔頂部的距離。

  3. 通過 document.documentElement.scrollTop 獲取瀏覽器窗口頂部與文檔頂部之間的距離,也就是滾動條滾動的距離。然後判斷 2-3<1 是否成立,如果成立,元素就在可視區域內。

element.offsetTop -  document.documentElement.scrollTop < document.documentElement.clientHeight

此方法也需要在滾動的時候去監聽圖片的位置,所以我們需要使用到window.onscroll事件,我們在事件內部處理相關的邏輯即可。

4、使用 IntersectionObserver

const observer = new IntersectionObserver(callback, observerConfig)
const imgList = document.querySelectorAll(".lazyload");
const observer = new IntersectionObserver(entries ={
    entries.forEach(item ={
        if (item.isIntersecting) {
            item.target.src = item.target.dataset.origin; // 判斷在可視區了,把data-origin的值放到src
            observer.unobserve(item.target); // 已經加載過的圖片停止進行監聽
        }
    });
});
imgList.forEach(item => observer.observe(item));

補充

1、優化

由於上面某些情況是需要使用到window.scroll事件的,所以我們可以增加節流來減少事件處理函數的調用次數。假設我們判斷是否可視區邏輯爲函數loadImage那麼我們可以如下處理。

window.onscroll = throttle(loadImage, 500)

2、拓展

上面後續三種方法不僅僅可以使用在圖片的懶加載上面,其實所有可以懶加載的地方都可以通過這種方式進行判斷,比如列表分頁加載,我們可以通過這種方式進行判斷是否需要進行下一頁的加載,比如我們需要埋曝光埋點的時候,可以通過這種方法判斷元素是否曝光,進行埋點事件的觸發。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/z588pAlBa5PWGpeEQebKNQ