為什麼需要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了,輕鬆又省事,但看了一下支援度 ,只能說慘不忍睹, 希望未來能有更多瀏覽器支援這個屬性
<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
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方法之外,還有提供以下的方法
unobserve() //停止觀察
disconnect() //取消觀察
當然如果覺得以上方法很麻煩的話,也可以使用套件 lazyload ,不過看了一下source code就會發現,套件也是利用IntersectionObserver來實現lazyloading
結論
看了一下IntersectionObserver支援度的部分已經能夠囊括大部分的瀏覽器, 如果不要管那些舊版本的瀏覽器的話, 用IntersectionObserver來實作lazy loading ,可以說是再適合不過 了,輕鬆又省事😃