為什麼需要lazy loading延遲載入?

其實一開始聽到 lazy loading 的反應是…

為甚麼需要延遲載入?

在網頁第一次載入時就將所有資源一口氣載入不是很好嗎? loading 跑完之後 就不會看到圖片慢慢載入的過程或是破圖 , 但實際情況是當使用者瀏覽網頁發現 loading 太久的時候就會失去耐性,甚至還沒 load 完就離開網頁了,所以應該要優先載入最重要的資源 ,其餘的部分再延遲載入。

這是個分秒必爭的時代

假如完整的資源有 10MB ,使用者需要等候 3 秒,但如果用了延遲載入 ,預先載 5MB 的資源 ,剩下的再慢慢載入 ,使用者只需要 1.5 秒就可以進入這個網頁 ,足足省下了一半的時間  ! 除此之外,在瀑布流的時候也會用到 lazy loading ,如果一口氣先撈好撈滿所有的圖片, 直接 Load 個一千張圖片, 畫面一定變的非常卡 ,而且用戶可能只是想進來看個幾張照片,結果後面那些多 load 的圖片就會變成浪費資源跟流量了。

以下就來介紹幾種 lazy loading 的作法

Native Lazy Loading

只要在 image tag 上加上 loading=’lazy’屬性就可以完成 lazy loading 了,輕鬆又省事,但看了一下支援度 ,只能說慘不忍睹, 希望未來能有更多瀏覽器支援這個屬性

1
<img src="/images/sky.jpg" loading="lazy" />

利用 scroll 或是 resize  來監聽事件

利用 scroll 、resize 事件來觀察目標對象是否出現在視窗中 ,再決定要不要載入圖片

首先為了先不要讓圖片載入, 將圖片連結先透過 data-src 屬性先存起來, 因為瀏覽器只要一解析到 img 的 src 有連結的話, 就會馬上載入圖片

解析步驟:

  1. 監聽 DOMContentLoaded 事件 ,當 DOM 都加載完畢後會執行事件

  2. 先取得所有 class 為 lazy 的圖片,透過 getBoundingClientRect() 來知道目標對象距離視窗頂端有多遠,判斷圖片是否已經顯示在畫面當中了

  3. 進入顯示範圍的話就把圖片連結從 data-src 取出, 然後賦值給 src,並且當所有圖片完成載入後, 移除 scroll 的監聽事件

  4. 用 setTimeout 避免頻繁觸發判斷的 function, 不然只是輕輕的滑一下, 就會造成同一張圖片瞬間被載入多次

但即便如此,每次滑動還是會頻繁地觸發 getBoundingClientRect 方法, 因此這個方法還是比較吃效能,較不建議使用

IntersectionObserver

用攝影來比喻的話, scroll 事件監聽就像是你需要一直盯著觀景窗看,然後手動去按快門,而 IntersectionObserver 則是當被攝對象進入觀景窗的時候就會自動拍攝,讓瀏覽器自動來幫你監看,比起以往的寫法,簡單了許多

首先先建立一個 new IntersectionObserver 觀察器,接下來再讓 observer 去觀察你指定的 DOM

1
2
3
4
5
6
7
8
9
10
const observer = new IntersectionObserver((entries) => {
//doSomeThing
}, option);

observer.observe(DOM_A);

// 如果需要監看多個只要依序 call observe 方法即可

observer.observe(DOM_B);
observer.observe(DOM_C);

IntersectionObserver 可以傳入 callback 和 option,callback 會在目標進入範圍 以及離開範圍的時候觸發

callback 會帶入一個參數,console.log 這個參數(entries)會發現是一個陣列 ,也就是那些你觀察的對象

任意點開一個對象, 會發現提供了這些資訊

  • intersectionRatio : 當目標對象出現多少可見比例, 完全出現時值為 1
  • target : 目標對象的 html tag
  • isVisible :目標對象是否可見
  • intersectionRect 目標對象與(根元素)觀景窗的交互資訊
  • boundingClientRect: 目標對象的資訊(top 、right、 bottom、 left…)
  • isIntersecting :是否已經出現在觀景窗中
  • rootBounds : 觀景窗的資訊(top 、right、 bottom、 left…)
  • time:被觸發的時間戳

那麼 option 可以傳入哪些屬性?

  • root: null (目標對象的父容器,null 等同 window)
  • rootMargin: “0px 0px 0px 0px” (各代表 top 、right、 bottom、 left ,觀景窗的偏移 ,一般來說我們不太會等到圖片出現在畫面中才開始 load 圖 , 一定是等到快要顯示的時候就開始載入, 假設設定為 top:200px 好了, 圖片就會在距離視窗還有 200px 的時候就開始載入*)*
  • threshold: [0] (當目標對象的可見範圍為 0%時執行 callback ,也可以設置為 0.5(等同於 50% ),那麼就是目標對象出現一半之後才會觸發 callback)

以下就實際用 IntersectionObserver 來實作一次 lazy loading

  • 建立完 IntersectionObserver 之後先跑迴圈, 將每張圖片都綁定 observer
  • 等到進入可見範圍內之後會觸發 callback function,再讀取 data-src 屬性,將圖片連結指定給 img src
  • 取消觀察目標對象

IntersectionObserver 除了 observe 方法之外,還有提供以下的方法

1
2
unobserve(); //停止觀察
disconnect(); //取消觀察

當然如果覺得以上方法很麻煩的話,也可以使用套件 lazyload ,不過看了一下 source code 就會發現,套件也是利用 IntersectionObserver 來實現 lazyloading

結論

看了一下 IntersectionObserver 支援度的部分已經能夠囊括大部分的瀏覽器,如果不要管那些舊版本的瀏覽器的話,用 IntersectionObserver 來實作 lazy loading,可以說是再適合不過了,輕鬆又省事 😃