Image and background-image Lazy Load using Javascript
前言
現在Landing Page正當道,單一網頁都長長的,圖片也用得多
對用戶而言,他一進入網站,在用戶的可視範圍外,明明他就還沒看到圖片,瀏覽器就在背景作業發出Request去下載
在瀏覽器發送 Request 的時候,瀏覽器會限制同一時間發出的 Request 數量,如果當網站的資源很多時,瀏覽器會依照限制數量分批下載
這樣就會造成後續的圖片、.js、.css…等等其他靜態資源阻塞佇列排隊中,形成網頁前端的效能瓶頸
其實只要在用戶操作瀏覽器捲軸往下捲動快要到達圖片位置時,瀏覽器才去下載圖片,這樣就能避免同一時間瀏覽器大量發送 Request 增加網站&用戶的等待時間
Image Lazy Load技術目的就是為了避免瀏覽器同一時間發出太多 Request 的前端解決方案,如此才能加快網頁載入速度
上圖為某網站使用Chrome開發者工具的觀察結果,稍微解釋一下各數據
DOMContentLoaded:和網頁HTML複雜度有關,愈複雜載入時間愈久
Load:網頁多久載入完成(包含HTML文件、.js、.css、img等等靜態資源),使用者多久可以看到網頁
Finish:通常和Load的數據差不多(但會多一些些),但如果網頁載入(Load)後,有發出Ajax或再度下載資源,Finish時間會再度增加,但DOMContentLoaded、Load的秒數不變
通常花費時間由小至大:DOMContentLoaded < Load < Finish
其他數據說明請見Goolge官網:View the timing breakdown of a request
本文選用的Javascript套件是Lazysizes plugin:https://github.com/aFarkas/lazysizes
其優點支援CSS背景圖片也能Lazy Load,而且引用它的JS Library後,只要再修改一下<img>的HTML語法即可,不必額外多寫Javascript代碼,使用方法簡單,也沒相依jQuery
Youtube上有老外教學影片:How to lazyload images by Alex Carpenter
實作
先從<img> 如何Lazy Load開始講起
第一步,先到Lazysizes plugin:https://github.com/aFarkas/lazysizes 下載「lazysizes.min.js script」,並加入自己的工作專案上
把lazysizes.min..js引入自己的網頁上,接著修改想要Lazy Load(延遲載入)的圖片HTML代碼
src改成data-src,瀏覽器一解析到此圖片沒有src屬性便不會發出Request,data-src要給JS套件抓圖片網址,套件會自動產生圖片的src屬性並給予data-src的網址
class加上lazyload關鍵字,套件才知道哪一張圖要有Lazy Load效果
以下是完整代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--icon setting -->
<link rel="icon" type="image/png" href="Content/img/favorite.ico" />
<title>Image Lazy Load</title>
<style type="text/css">
html, body {
height: 100%;
}
img {
display: block; /*圖片自動換行*/
height: 400px;
margin-bottom: 600px; /*每張圖的間距*/
}
</style>
</head>
<body>
<!--通常第一張圖片,用戶一進入網站就必須馬上看得到,不需要Lazy Load-->
<img src="Content/img/1.jpg" />
<!--第二張圖之後(用戶可視範圍外)才需要Lazy Load-->
<img data-src="Content/img/2.jpg" class="lazyload" />
<img data-src="Content/img/3.jpg" class="lazyload" />
<img data-src="Content/img/4.jpg" class="lazyload" />
<img data-src="Content/img/5.jpg" class="lazyload" />
<img data-src="Content/img/6.jpg" class="lazyload" />
<img data-src="Content/img/7.jpg" class="lazyload" />
<img data-src="Content/img/8.jpg" class="lazyload" />
<!--引用lazysizes-->
<script src="Content/js/lazysizes.min.js"></script>
</body>
</html>
程式執行結果:
原本這樣↓
圖片Lazy Load延遲載入後變成這樣↓
本文為了展示,特地選用好幾MB的圖片來測試效能,實際上超過150KB圖片記得要先壓縮後再放入工作專案裡
否則大圖檔就算使用Lazy Load技術可能會讓用戶看到以下被截斷的情形
壓縮圖片的程式碼請見我另一篇文章:[C#.Net] 壓縮圖片,指定JPG的壓縮品質
另外提醒一下,實作Lazy Load Image的網頁,如果用戶瀏覽器Ctrl+S儲存完整網頁到他的電腦中,用戶下載的圖片是有可能缺圖的(因為當下網頁沒載入全部圖片)
用戶必須把瀏覽器捲軸捲到最下方,待網頁載入全部圖片後再按Ctrl+S→「儲存完整網頁」才能把網頁的全部圖片下載至他自己的電腦
官網有提供CSS API 讓圖片載入時有動畫淡入淡出效果,不過這套件貌似並非使用者捲動到圖片位置時才下載圖片,而會先行下載下一張圖
這樣做動畫效果就沒意義,因為使用者也看不到動畫,所以乾脆作罷不介紹XD
20191120追記
最近工作上Lazy Load Image效果要和 jQuery bxSlider圖片輪播套件整合,試了好久,參考以下這篇,總算成功
How do I integrate lazyload & bxslider together?
只不過這套件要自己寫背景圖的Lazy Load
//add simple support for background images lazyload:
document.addEventListener('lazybeforeunveil', function (e) {
var bg = e.target.getAttribute('data-bg');
if (bg) {
e.target.style.backgroundImage = bg;
}
});
HTML代碼↓
<section class="lazyload" data-bg="url('../images/idx_advisory.jpg')">
</section>
背景圖片的Lazy Load延遲載入
出自 Youtube影片:How to lazyload background images
同個作者套件,去官網:lazysizes bgset extension - responsive background images
把ls.bgset.min.js加入自己的工作專案並引用進網頁中,接著修改背景圖的容器HTML代碼
↑加入class關鍵字"lazyload"、background-image移除改成data-bgset,使用邏輯和<img>差不多
完整程式碼↓
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--icon setting -->
<link rel="icon" type="image/png" href="Content/img/favorite.ico" />
<title>Background Image Lazy Load</title>
<style type="text/css">
html, body {
height: 100%;
}
div {
background-size:cover;
background-position:center center;
height: 400px;
margin-bottom: 600px; /*每個div的間距*/
}
</style>
</head>
<body>
<!--通常第一張圖片,用戶一進入網站就必須馬上看得到,不需要Lazy Load-->
<div style="background-image:url(Content/img/1.jpg)"></div>
<!--第二張圖之後(用戶可視範圍外)才需要Lazy Load-->
<div class="lazyload" data-bgset="Content/img/2.jpg"></div>
<div class="lazyload" data-bgset="Content/img/3.jpg"></div>
<div class="lazyload" data-bgset="Content/img/4.jpg"></div>
<div class="lazyload" data-bgset="Content/img/5.jpg"></div>
<div class="lazyload" data-bgset="Content/img/6.jpg"></div>
<div class="lazyload" data-bgset="Content/img/7.jpg"></div>
<div class="lazyload" data-bgset="Content/img/8.jpg"></div>
<!--引用順序我試沒什麼差別,但還是照官網寫吧-->
<script src="Content/js/ls.bgset.min.js"></script>
<!--引用lazysizes-->
<script src="Content/js/lazysizes.min.js"></script>
</body>
</html>
注意:這個背景圖Lazy Load套件會同一張圖送出Request下載兩次,瀏覽器寬度大小拉著變動(雖然很少使用者這樣做),圖片也會再度多重下載
不知道是不是因為這樣效能不太好的原因,作者建議棄用此套件(ls.bgset.js),所以使用上請斟酌一下,如果你的網頁中有太多背景圖,請不要使用此套件,否則適得其反
至於背景圖Lazy Load,如果我有找到更好的套件,日後有空再補上
2019-10-20 補充
此作者的背景圖片Lazy Load效能比較好:https://github.com/verlok/lazyload (vanilla-lazyload)
Youtube教學影片:Lazy Loading Images by iEatWebsites
使用方法如下,完整程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--icon setting -->
<link rel="icon" type="image/png" href="Content/img/favorite.ico" />
<title>Background Image Lazy Load</title>
<style type="text/css">
html, body {
height: 100%;
}
div {
background-size: cover;
background-position: center center;
height: 400px;
margin-bottom: 600px; /*每個div的間距*/
}
</style>
</head>
<body>
<!--通常第一張圖片,用戶一進入網站就必須馬上看得到,不需要Lazy Load-->
<div style="background-image:url(Content/img/1.jpg)"></div>
<!--第二張圖之後(用戶可視範圍外)才需要Lazy Load-->
<div class="lazy" data-bg="url(Content/img/2.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/3.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/4.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/5.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/6.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/7.jpg)"></div>
<div class="lazy" data-bg="url(Content/img/8.jpg)"></div>
<script src="https://cdn.jsdelivr.net/npm/intersection-observer@0.7.0/intersection-observer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.1.0/dist/lazyload.min.js"></script>
<!--↓多了一個JS步驟-->
<script type="text/javascript">
var lazyLoadInstance = new LazyLoad({
elements_selector: ".lazy" //指定哪個element要Lazy Load
});
</script>
</body>
</html>
他的圖片Lazy Load的完整程式碼(含轉場動畫淡入效果)↓
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--icon setting -->
<link rel="icon" type="image/png" href="Content/img/favorite.ico" />
<title>Image Lazy Load</title>
<style type="text/css">
html, body {
height: 100%;
}
img {
display: block; /*圖片自動換行*/
border: 0;
height: 500px;
margin-bottom: 200px; /*圖片間距*/
}
</style>
<!--Lazy Load Image的fadeIn轉場效果CSS-->
<style>
img.lazy {
opacity: 0; /*Lazy Load的圖片預設透明*/
}
img.loaded,
img.error {
transition: opacity 1s;
}
img.initial,
img.loaded,
img.error {
opacity: 1;
}
</style>
</head>
<body>
<!--通常第一張圖片,用戶一進入網站就必須馬上看得到,不需要Lazy Load-->
<img src="Content/img/1.jpg" />
<!--第二張圖之後(用戶可視範圍外)才需要Lazy Load-->
<img data-src="Content/img/2.jpg" class="lazy" />
<img data-src="Content/img/3.jpg" class="lazy" />
<img data-src="Content/img/4.jpg" class="lazy" />
<img data-src="Content/img/5.jpg" class="lazy" />
<img data-src="Content/img/6.jpg" class="lazy" />
<img data-src="Content/img/7.jpg" class="lazy" />
<img data-src="Content/img/8.jpg" class="lazy" />
<script src="https://cdn.jsdelivr.net/npm/intersection-observer@0.7.0/intersection-observer.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.1.0/dist/lazyload.min.js"></script>
<!--↓多了一個JS步驟-->
<script type="text/javascript">
var lazyLoadInstance = new LazyLoad({
elements_selector: ".lazy", //指定哪個element要Lazy Load
threshold:0 //數字太大就會當捲軸距離下一張圖片還很遠時,下一張圖就預先被下載,使用者看不到動畫fadeIn效果
});
</script>
</body>
</html>
↑此JS套件也支援 HTML5 <video>影片LazyLoad,不過我沒看到 poster 屬性的 lazyLoad 方式
結語
雖然Chrome在76版之後可以針對<img>有加上loading="lazy"屬性的圖片做延遲載入:
Youtube:Lazy Loading Images without JavaScript!
Native lazy-loading for the web | web.dev
但為了瀏覽器兼容性,而且我也不確定這樣能否通過工具的效能檢測(畢竟Chrome才剛支援,還很新的東西)
所以還是自己找個JS Plugin來使用吧~