為什麼需要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 有連結的話, 就會馬上載入圖片
解析步驟:
監聽 DOMContentLoaded 事件 ,當 DOM 都加載完畢後會執行事件
先取得所有 class 為 lazy 的圖片,透過 getBoundingClientRect() 來知道目標對象距離視窗頂端有多遠,判斷圖片是否已經顯示在畫面當中了
進入顯示範圍的話就把圖片連結從 data-src 取出, 然後賦值給 src,並且當所有圖片完成載入後, 移除 scroll 的監聽事件
用 setTimeout 避免頻繁觸發判斷的 function, 不然只是輕輕的滑一下, 就會造成同一張圖片瞬間被載入多次
但即便如此,每次滑動還是會頻繁地觸發 getBoundingClientRect 方法, 因此這個方法還是比較吃效能,較不建議使用
IntersectionObserver
用攝影來比喻的話, scroll 事件監聽就像是你需要一直盯著觀景窗看,然後手動去按快門,而 IntersectionObserver 則是當被攝對象進入觀景窗的時候就會自動拍攝,讓瀏覽器自動來幫你監看,比起以往的寫法,簡單了許多
首先先建立一個 new IntersectionObserver 觀察器,接下來再讓 observer 去觀察你指定的 DOM
1 | const observer = new IntersectionObserver((entries) => { |
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 | unobserve(); //停止觀察 |
當然如果覺得以上方法很麻煩的話,也可以使用套件 lazyload ,不過看了一下 source code 就會發現,套件也是利用 IntersectionObserver 來實現 lazyloading
結論
看了一下 IntersectionObserver 支援度的部分已經能夠囊括大部分的瀏覽器,如果不要管那些舊版本的瀏覽器的話,用 IntersectionObserver 來實作 lazy loading,可以說是再適合不過了,輕鬆又省事 😃