網頁載入時,最耗費流量的資源大概就是圖片了。為了節省流量的浪費,除了將圖片在可接受畫質的狀況下,盡可能的將檔案大小進行壓縮之外,另一個實用技巧就是延遲載入 (Lazy loading)。

有關圖片壓縮方法,可以參考部落格中的另外一篇文章,下方為文章連結。

延遲載入解決了什麼問題呢?

我們假設有一個使用了 50 張圖片的網頁,正常情況下,當使用者進入網頁後,瀏覽器會等這 50 張圖片都載入完成才會顯示畫面,試想在不捲動右邊卷軸的狀況下,我們第一眼從畫面中大概只能看到最前面的 4 到 5 張圖而已,可是卻要等全部 50 張圖都載入完成才能觀看。 再假設一張圖片只要 500KB 就好,這樣首次進入網頁就要載入高達 25MB 的圖片,而且還要加上其他 CSS 及 JavaScript 等必要檔案,這明顯是很浪費流量的事情。 使用延遲載入就能解決這個問題,讓瀏覽器先載入使用者在當前畫面會看到圖片就好,等到使用者往下捲動時才載入下方的圖片。

延遲載入能帶來什麼好處了?

我認為延遲載入最明顯的好處,有以下三點:

節省流量
這是一個顯而易見的好處,原本一次要載入全部圖片,現在只需要載入必要圖片,明顯可以節省很多流量的消耗。
降低使用者因等待而跳出
Google研究指出網頁載入在 1~3 秒內,使用者跳出畫面可能性為 32%,超過 5 秒可能性就會大幅增加到 90%,這是一個非常驚人的數據,因此讓使用者越快看到畫面是非常重要的。
提升 SEO 成效
Google 在 2010 年早就公布網站的執行速度將會影響搜尋排名,並且這是屬於中高強度的排名訊號,重要性不言而喻。

延遲載入方法

方法一:使用套件

圖片延遲載入這個議題,從很久以前就已被廣泛討論,因此有很多免費套件都能達成此功能,這邊簡單使用其中一個套件來製作範例程式,以下為範例程式所使用的套件網址。

以下為範例程式碼,沒有太複雜的程式語法,非常簡單易用。

                
                    <script src="https://cdn.jsdelivr.net/npm/lazyload@2.0.0-rc.2/lazyload.js"></script>

                    <img src="loading.svg" data-src="image1.jpg"class="lazyload">
                    <img src="loading.svg" data-src="image2.jpg"class="lazyload">
                    <img src="loading.svg" data-src="image3.jpg"class="lazyload">
                    <img src="loading.svg" data-src="image4.jpg"class="lazyload">
                    <img src="loading.svg" data-src="image5.jpg"class="lazyload">

                    <script type="text/javascript">
                        let images = document.querySelectorAll('img.lazyload');
                        lazyload(images);
                    </script>
                
            

方法二:使用 Intersection Observer API

如果不喜歡用套件,喜歡自己動手做的話,其實也不會太難,大致上步驟流程如下:

  1. 先不要讓圖片正常載入畫面。
  2. 監聽圖片是否進入到可視範圍。
  3. 圖片進入可視範圍,再載入圖片。

先來看看一個完整的範例

                
                    <img src="loading.svg" data-src="image.png" class="lazy">

                    <script src="https://cdn.jsdelivr.net/npm/intersection-observer@0.12.2/intersection-observer.min.js"></script>
                    <script type="text/javascript">
                        const observer = new IntersectionObserver((entries, owner) => {
                            for (let entry of entries) {
                                if (entry.isIntersecting) {
                                    // 圖片進入畫面再載入真實照片
                                    const img = entry.target;
                                    img.setAttribute('src', img.dataset.src);
                                    img.removeAttribute('data-src');
                                    owner.unobserve(img);
                                }
                            }
                        });
                        const images = document.querySelectorAll('img.lazy')
                        for (let image of images) {
                            observer.observe(image);
                        }
                    </script>
                
            
  • 行 1:使用 data-src 屬性設定真正要載入的圖片路徑,並用 src 屬性顯示一張載入動畫,讓圖片載入時,不會顯示一片空白,class="lazy" 讓 JavaScript 知道這張圖片要做延遲載入。
  • 行 3:目前有支援 Intersection Observer API 的瀏覽器已高達 90% 以上,如果真的擔心有一些老舊瀏覽器不支援的話,可以載入 polyfill 就不用擔心了。
  • 行 5、行 16 ~ 19:使用 Intersection Observer API 監聽圖片是否進入到可視範圍,也有人提到可以監聽 scroll、resize 和 orientationchange 事件來達成,但我認為這不是一個好的做法,想想只要稍微滑一下畫面,就會一直重複觸發相同的事件處理,感覺就很吃效能。
  • 行 6 ~ 14:當圖片進入可視範圍,就將 src 屬性換成 data-src 屬性的圖片路徑,並且刪除 data-src 屬性及取消監聽。

以上就是完整的範例,有關 Intersection Observer API 在瀏覽器的支援狀況,以及範例中所使用到的 polyfill,可以參考以下網址。

方法三:使用瀏覽器原生支援

使用瀏覽器原生支援大概是最簡單達成延遲載入的方法,您甚至一行 JavaScript 程式碼都無需撰寫就能達成此功能。 只要使用 img 標籤的 loading 屬性,讓瀏覽器自動幫您完成此功能即可,超級簡單。

                
                    <img src="image.jpg" loading="lazy">
                
            

loading 屬性一共有兩種設定值可供選擇,分別是:
eager:立即載入圖片,無論圖片目前是否在可視範圍,這也是瀏覽器的預設值。
lazy:延遲載入圖片,直到圖片達到與瀏覽器定義的可視範圍距離才載入圖片。

此種方式雖然簡單,但並不是所有的瀏覽器都有原生支援,您可以透過下方網址來了解目前主流瀏覽器的支援狀況。 不過目前語法設計具備向下兼容,就算在瀏覽器不支援的狀況下,只是沒有延遲載入功能而已,並不會影響圖片的呈現。