JavaScript優(yōu)化圖片懶加載的性能技巧
背景
前端發(fā)展過程中有許多性能優(yōu)化的操作,比如防抖、節(jié)流和圖片懶加載等。在這里我們首先聊聊圖片懶加載操作。
在最近的618中,我們會經(jīng)常逛像淘寶和京東等購物平臺。那你覺得在淘寶頁面中的圖片資源是打開頁面時就一次性全部加載完了呢,還是在你滾輪滾動到的區(qū)域才加載圖片呢。
一次性全部加載會導致加載時間長、網(wǎng)絡資源消耗大、內存占用率高和發(fā)出大量圖片請求給服務器帶來的巨大壓力。
所以采用的是只加載當前可見區(qū)域的圖片,隨著用戶的滾動,當其他圖片進入可見區(qū)域時,再進行加載。這種方法就是圖片的懶加載。這種方式可以有效地提高頁面的響應速度,特別是在圖片數(shù)量較多或網(wǎng)絡條件較差的情況下,可以避免頁面加載緩慢或卡頓的現(xiàn)象,提供更好的用戶體驗。
如下圖所示,淘寶頁面剛打開時并不是全部加載的。
大致思路
圖片懶加載布局邏輯:
- 通過將
img
標簽的src
屬性值都設置為同一個圖片的url,這個圖片需要盡可能小。這樣即可以為后續(xù)要真正加載的圖片占位置,也可以讓頁面布局更快地呈現(xiàn)出來,而不是長時間等待圖片加載導致空白。 - 將每一個
img
標簽中真正的圖片url存放在一個數(shù)據(jù)屬性當中。當需要加載時將該數(shù)據(jù)屬性內的內容賦值給img
標簽的src
屬性。
圖片懶加載交互邏輯:
- 首先通過獲取用戶可視窗口高度、滾輪到最頂端的距離和每個圖片到最頂端的距離。
- 通過監(jiān)聽器監(jiān)聽滾輪滾動事件觸發(fā)懶加載函數(shù)。
- 但是一開始就出現(xiàn)在可視窗口的圖片需要直接加載,所以需要先運行一次懶加載函數(shù)。
- 在懶加載函數(shù)里通過判斷(圖片到最頂端的距離)和(用戶可視窗口高度+滾輪到最頂端的距離)的大小判斷是否需要加載。
編輯代碼
html部分
<img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/hsossms/20240612/v2_8ec7812750194dbd831babce8806c626@000000_oswg5522709oswg1792oswg1024_img_png?x-oss-process=image/resize,m_mfit,w_600,h_400,limit_0/crop,w_600,h_400,g_center/format,webp" /> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190808/v2_1565254363234_img_jpg"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567641293753_img_png"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567640518658_img_png"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567642423719_img_000"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567642425030_img_000"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567642425101_img_000"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567642425061_img_000"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190904/v2_1567591358070_img_jpg"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567641974410_img_000"> <img src="https://misc.360buyimg.com/mtd/pc/common/img/blank.png" data-src="https://img.36krcdn.com/20190905/v2_1567641974454_img_000">
在html部分中有一個重要的步驟。你會發(fā)現(xiàn)每一個img
標簽的src
屬性值都是一樣的,我們可以復制鏈接到瀏覽器查看該圖片。
該圖片是中間的小白點:
該圖片的屬性如下:
可以看出這個圖片是非常小的。在頁面打開時,加載的都是這個圖片,真正要加載的圖片url放置在data-src
數(shù)據(jù)屬性里面。最后通過JavaScript部分實現(xiàn)將data-src
數(shù)據(jù)屬性的內容賦值給img
標簽的src
屬性,然后發(fā)送HTTP請求加載真正的圖片。
css部分
img { display: block; margin-bottom: 50px; width: 400px; height: 400px; } body { background-color: gray; }
通過設置img
標簽樣式,讓小圖片給真正要放的圖片占位置。
JavaScript部分
- 變量定義:其中
imgs
是包含頁面中所有<img>
元素的集合,可以通過索引來訪問具體的圖像元素;num
表示圖片數(shù)量;n
記錄被加載的圖片數(shù)量。
const imgs = document.getElementsByTagName('img'); const num = imgs.length; let n = 0
- 在全局一個設置
scroll
事件監(jiān)聽器,當事件觸發(fā)后會調用lazyload
慢加載函數(shù)。
window.addEventListener('scroll', lazyload)
- 定義一個
lazyload
慢加載函數(shù):
function lazyload() { //可視區(qū)域的高度 let screenHeight = document.documentElement.clientHeight; //滾動條距離最頂部的距離, let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //判斷圖片是否存在在可視區(qū)域內 for (let i = n; i < num; i++) { if (imgs[i].offsetTop > scrollTop + screenHeight) { break; } else { //主動觸發(fā)下載 imgs[i].src = imgs[i].getAttribute('data-src'); //記錄已經(jīng)加載過的圖片數(shù)量 n = i + 1; if (n === num) { //全部加載完畢后移除滾動事件 window.removeEventListener('scroll', lazyload); } } } }
首先獲取可視窗口的高度
screenHeight
、滾動條距離最頂部的距離scrollTop
和具體圖片距離最頂端的距離imgs[i].offsetTop
。通過for循環(huán)遍歷每個img
元素。- 當
imgs[i].offsetTop > scrollTop + screenHeight
時,也就是說該圖片的區(qū)域并沒有被可視區(qū)域覆蓋過,所以圖片不需要加載,也就沒有別的后續(xù)操作了。 - 如果
imgs[i].offsetTop <= scrollTop + screenHeight
時,說明該圖片的區(qū)域被可視區(qū)域覆蓋了,圖片需要進行加載。
- 當
當圖片需要加載時,將真正的圖片url賦值給
src
屬性。由imgs[i].src = imgs[i].getAttribute('data-src')
實現(xiàn)。每次加載一張圖片
n
的值就加一。當n
的值等于num
的值時,也就是所有圖片都加載完成后就不需要scroll
事件監(jiān)聽器了,所有通過window.removeEventListener('scroll', lazyload)
清除監(jiān)聽器。最后因為首屏內的圖片需要直接加載,而不是通過
scroll
事件監(jiān)聽器實現(xiàn)加載。所有需要調用一次慢加載函數(shù)。
//方法一 document.addEventListener('DOMContentLoaded', lazyload) //方法二 window.addEventListener('load', lazyload)
方法一通過
DOMContentLoaded
事件監(jiān)聽器觸發(fā)慢加載函數(shù)的速度比方法二通過load
事件監(jiān)聽器觸發(fā)慢加載函數(shù)的速度快。所以推薦方法一。
節(jié)流優(yōu)化
因為scroll
事件監(jiān)聽器在頻繁的滑輪滾動會頻繁觸發(fā)。如果直接在事件處理函數(shù)中執(zhí)行大量復雜的操作,可能會導致性能問題。
所以通過使用節(jié)流限制事件觸發(fā)的頻率。
const imgs = document.getElementsByTagName('img'); const num = imgs.length; //用變量記錄節(jié)流返回的函數(shù) const throttleLazyLoad = throttle(lazyload, 200); //滾動事件觸發(fā)懶加載 window.addEventListener('scroll', throttleLazyLoad) let n = 0 //首屏加載,DOMContentLoaded事件是DOM加載完成,不包括圖片(比load事件快) document.addEventListener('DOMContentLoaded', lazyload) //首屏加載,load事件是DOM加載完成,包括圖片(比DOMContentLoaded事件慢) window.addEventListener('load', lazyload) //懶加載函數(shù) function lazyload(event) { //可視區(qū)域的高度 let screenHeight = document.documentElement.clientHeight; //滾動條距離最頂部的距離, let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //判斷圖片是否存在在可視區(qū)域內 for (let i = n; i < num; i++) { if (imgs[i].offsetTop > scrollTop + screenHeight) { break; } else { //主動觸發(fā)下載 imgs[i].src = imgs[i].getAttribute('data-src'); //記錄已經(jīng)加載過的圖片數(shù)量 n = i + 1; if (n === num) { //全部加載完畢后移除滾動事件 window.removeEventListener('scroll', throttleLazyLoad); } } } } //節(jié)流函數(shù) function throttle(func, limit) { let inThrottle; return function () { const context = this; const args = arguments; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; }
定義了一個節(jié)流函數(shù),它接收要執(zhí)行的函數(shù) func
和時間間隔限制 limit
。內部通過一個變量 inThrottle
來標記當前是否處于節(jié)流狀態(tài)。當執(zhí)行返回的函數(shù)時,先判斷如果不在節(jié)流狀態(tài),就立即執(zhí)行目標函數(shù),并將 inThrottle
設置為 true,同時使用 setTimeout
在指定時間間隔后將 inThrottle
恢復為 false,從而實現(xiàn)了在規(guī)定時間間隔內只執(zhí)行一次函數(shù)的節(jié)流效果,避免了頻繁觸發(fā)導致的性能問題。
再次優(yōu)化
在日常工作時,如果讓你選擇手搓一個節(jié)流函數(shù)和直接使用工具庫里的函數(shù),你肯定也會和我一樣偷懶,選擇直接使用工具庫里的現(xiàn)成的函數(shù)。
首先在HTML中使用以下代碼引入 Lodash
庫。
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
然后可以刪除掉你手搓的節(jié)流函數(shù)了,使用 Lodash
庫里的節(jié)流函數(shù)。
const imgs = document.getElementsByTagName('img'); const num = imgs.length; //用變量記錄節(jié)流返回的函數(shù) const throttleLazyLoad = _.throttle(lazyload, 200);//調用Lodash庫里的節(jié)流函數(shù) //滾動事件觸發(fā)懶加載 window.addEventListener('scroll', throttleLazyLoad) let n = 0 //首屏加載,DOMContentLoaded事件是DOM加載完成,不包括圖片(比load事件快) document.addEventListener('DOMContentLoaded', lazyload) //首屏加載,load事件是DOM加載完成,包括圖片(比DOMContentLoaded事件慢) window.addEventListener('load', lazyload) //懶加載函數(shù) function lazyload(event) { //可視區(qū)域的高度 let screenHeight = document.documentElement.clientHeight; //滾動條距離最頂部的距離, let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //判斷圖片是否存在在可視區(qū)域內 for (let i = n; i < num; i++) { if (imgs[i].offsetTop > scrollTop + screenHeight) { break; } else { //主動觸發(fā)下載 imgs[i].src = imgs[i].getAttribute('data-src'); //記錄已經(jīng)加載過的圖片數(shù)量 n = i + 1; if (n === num) { //全部加載完畢后移除滾動事件 window.removeEventListener('scroll', throttleLazyLoad); } } } }
呈現(xiàn)效果
當所有圖片加載完后滾動事件就不會觸發(fā)慢加載函數(shù)了,并且也有節(jié)流效果。
首屏的圖片立即加載,區(qū)域圖片在滾動到再加載。
以上就是JavaScript優(yōu)化圖片懶加載的性能技巧的詳細內容,更多關于JavaScript優(yōu)化圖片懶加載的資料請關注腳本之家其它相關文章!
相關文章
JavaScript實現(xiàn)簡單獲取當前網(wǎng)頁網(wǎng)址的方法
這篇文章主要介紹了JavaScript實現(xiàn)簡單獲取當前網(wǎng)頁網(wǎng)址的方法,通過location對象的href方法來獲取網(wǎng)址,非常簡單實用,需要的朋友可以參考下2015-11-11