關於惰性加載
在講圖片的惰性加載前,我們先來聊聊惰性加載。惰性加載又稱為延遲加載、懶加載等,還有個好聽的英文名字叫做 "lazyload"。需要注意的是,惰性加載並不只是圖片的專利,Javascript 中函數也有惰性加載的概念,而在 Javascript 異步加載中還有個 LazyLoad類庫,而圖片的惰性加載庫(lazyload)與之完全是兩個概念,這些一定要弄清楚,以免混淆概念。
圖片的惰性加載是啥意思?為什么要用它?當我們頁面上的東西越來越豐富的時候,我們發現頁面的加載速度卻越來越慢,而圖片的加載量無疑是 HTTP 請求里面的大頭。其實很多時候,你把整個頁面加載完,用戶卻不會滑動到最下面,也就是說很多東西其實白白加載了。因為圖片的加載是大頭,所以我們先拿圖片開刀,我們假設,如果試圖加載一個 HTML 頁面,圖片先不加載,當用戶將頁面往下滑動,圖片該出現在可視區域時,再將該圖片加載,這樣就能將一開始打開頁面的 HTTP 請求量降低,這就是圖片的惰性加載。
實現
圖片的惰性加載實現方式其實很簡單。
- 在 HTML 文件中將需要惰性加載的圖片的 src 屬性置為一個相同的地址(一般設置為一張 loading 圖),這樣這張圖只會加載一次(第二次即會讀取緩存),或者干脆置為空(用戶體驗不好),將真實的圖片地址存儲在別的屬性中(比如 data-original 屬性)
- 監聽事件(比如 scroll 事件),判斷需要惰性加載的圖片是否已經在可視區域,如果是,則將 src 屬性替換成 data-original 屬性值
接着我們來簡單寫下代碼。
首先,按照第一步將真實的圖片地址藏在 data-original 屬性中。這里我假設所有圖片都要進行惰性加載,現實開發中如果肯定是在第一屏的圖片,它的 src 完全可以直接指向真實的地址。

<ul> <li class='lazy'><img data-original='images/0.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/1.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/2.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/3.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/4.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/5.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/6.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/7.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/8.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/9.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/10.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/11.jpg' src='images/loading.gif'/></li> <li class='lazy'><img data-original='images/12.jpg' src='images/loading.gif'/></li> </ul>
因為我把所有圖片都設置為惰性加載模式,而首屏的圖片需要直接顯示,這里我寫了個 init() 函數,注釋都在代碼中了:

function init() { var images = document.images; // 加載首屏圖片 for (var i = 0, len = images.length; i < len; i++) { var obj = images[i]; // 如果在可視區域並且還沒被加載過 if (obj.getBoundingClientRect().top < document.documentElement.clientHeight && !obj.isLoad) { obj.isLoad = true; // 先調用 HTML5 方法 if (obj.dataset) imageLoaded(obj, obj.dataset.original); else imageLoaded(obj, obj.getAttribute('data-original')); } else { // 假設圖片標簽在 HTML 中的順序和實際頁面中順序一致 break; } } }
代碼中寫了個 imageLoaded() 函數來將真實的圖片地址指向元素,如果直接將 data-original 屬性值指向圖片的 src 屬性的話,看到的圖片可能會一段一段出現,而先將圖片完全加載,然后再賦值使圖片出現的話,體驗就好多了。
function imageLoaded(obj, src) { var img = new Image(); img.onload = function() { obj.src = src; }; img.src = src; }
OK,接着我們監聽 scroll 事件。當用戶滑動頁面,圖片出現在可視區域時,隨即加載圖片。
window.onscroll = function() { lazyload(); }; function lazyload() { var lazy = 0; var images = document.images; for (var i = 0, len = images.length; i < len; i++) { var obj = images[i]; if (obj.getBoundingClientRect().top - lazy < document.documentElement.clientHeight && !obj.isLoad) { obj.isLoad = true; if (obj.dataset) imageLoaded(obj, obj.dataset.original); else imageLoaded(obj, obj.getAttribute('data-original')); } } }
有的時候並不能當圖片剛好在可視區域的時候再去加載,而要稍微 "預加載",可以調整下 lazyload() 函數中的 lazy 參數。
如果 "生硬" 地顯示圖片體驗不大好,也可以搞點淡出效果,具體就不說了,可以看完整代碼 Github
這樣,一個簡單的圖片惰性加載 DEMO 就完成了!
PS:惰性加載雖然好處多多,但是也有一個 "非致命" 的缺點,影響 SEO。因為圖片都被替換成假的圖片,所以會影響圖片的收錄,所以這功能不建議在詳情頁使用