您好,登錄后才能下訂單哦!
這篇文章主要介紹“前端JS圖片懶加載原理是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“前端JS圖片懶加載原理是什么”文章能幫助大家解決問題。
圖片懶加載的原理是沒有在可視區域的圖片暫時不加載圖片,等進入可視區域后在加載圖片,這樣可以減少初始頁面加載的圖片數量而提升頁面加載速度。 圖片懶加載在提升頁面加載速度的同時也會伴隨用戶看其他未展示的圖片時會有等待時間;圖片加載顯示會伴有布局抖動等問題。
圖片懶加載的關鍵是:判斷一個元素是否在可視區域。
HTMLImageElement 的 loading 屬性為一個字符串,它的值會提示 用戶代理 告訴瀏覽器不在可視視口內的圖片該如何加載。這樣一來,通過推遲圖片加載僅讓其在需要的時候加載而非頁面初始載入時立刻加載,優化了頁面的載入。
lazy 告訴用戶代理推遲圖片加載直到瀏覽器認為其需要立即加載時才去加載。例如,如果用戶正在往下滾動頁面,值為 lazy 會導致圖片僅在馬上要出現在 可視視口中時開始加載。
<img src="xxx.jpg" loading="lazy" />
只設置一個屬性不用 JavaScript 控制代碼是最簡單方便的方案,性能也是比較好的。
大部分主流瀏覽器都兼容該屬性。
雖然整個方案簡單性能好,但問題也是最多的,所以很少使用這種方案。
前面提到圖片懶加載用戶看其他未展示的圖片時會有等待時間,一般會設置一個默認圖片,這種方案不能設置默認圖片
圖片的加載數量和圖片的布局、可視區域尺寸有關,難以控制
圖片的加載順序也難以控制
該方案能粗略的實現圖片懶加載基本功能。
可視區域高度是 document.documentElement.clientHeight
,而可視區域的位置是在滾動條滾動位置 scrollTop
到 scrollTop+document.documentElement.clientHeight
之間。因此通過 image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop
判斷圖片是否可以在可視區域內。
function lazyload() { var lazyImages = document.querySelectorAll(".lazyload"); lazyImages.forEach(function (image) { if ( image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop) { image.src = image.getAttribute("data-src"); } }); }
添加滾動條監聽。
window.onscroll = function () { lazyload(); };
html結構。
<img src="./default.gif" class="lazyload" data-src="./photo-1.jpg" />
上面只是簡單的實現圖片懶加載,在實際開發中還要很多細節需要優化: 首先是兼容性,這里有兩個點涉及到兼容性:document.documentElement.clientHeight
和document.documentElement.scrollTop
。 獲取瀏覽器窗口的內部高度方法有 window.innerHeight
、document.documentElement.clientHeight
。 window.innerHeight
兼容性是 ie9+ 和其他主流瀏覽器。
document.documentElement.clientHeight
瀏覽器都支持。
獲取滾動位置方法有 window.pageYOffset
和 document.documentElement.scrollTop
。 window.pageYOffset
兼容性是 ie9+ 和其他主流瀏覽器。
第二優化點是offsetTop。
offsetParent 元素有滾動條的情況下計算會不會有問題
HTMLElement.offsetTop 為只讀屬性,它返回當前元素相對于其 offsetParent 元素的頂部內邊距的距離。 ——MDN
offsetTop 是相對其 offsetParent 元素的并不是相對瀏覽器窗口可視區域的。如果圖片元素有 offsetParent 那么 offsetTop 是有偏差的。
function getBoundingClientTop(el) { let top = el.offsetTop; let parent = el.offsetParent; while (parent) { top += parent.offsetTop; parent = parent.offsetParent; } return top; }
第三優化點避免賦值 src 。 代碼是通過 lazyload 類獲取需要懶加載的元素,這樣會把之前已經加載圖片的元素也獲取到了,而重復設置 src屬性。
function lazyload() { var lazyImages = document.querySelectorAll(".lazyload[data-src]"); lazyImages.forEach(function (image) { if ( getBoundingClientTop(image) <= document.documentElement.clientHeight + document.documentElement.scrollTop ) { image.src = image.getAttribute("data-src"); image.removeAttribute("data-src") } }); }
通過 lazyload 類并且有 data-src 來獲取元素,src 設置完后移除 data-src 屬性來避免重復設置 src 。
第四優化點 onscroll 是否添加防抖。 onscroll 常用的優化點是加入防抖來減少事件觸發的頻率,但這里如果加了防抖,計算元素是否在可視區域內的精度就差很多,當滾動速度比較快的情況下加載反應不靈敏,這里就要找平衡點。
第五優化點頁面中局部的 div 滾動圖片懶加載。 除了整個頁面的滾動圖片懶加載,也有頁面中局部滾動圖片懶加載,就需要給制定的有滾動條 dom 元素綁定onscroll 事件。
srcollDom.onscroll = function () { lazyload(); };
并且獲取圖片 top 是相對有滾動條 dom 元素
getBoundingClientTop(image)-getBoundingClientTop(srcollDom) <= srcollDom.clientHeight + srcollDom.scrollTop
第六優化點加載圖片的時間點提前。 代碼中是圖片元素進入可視區域后才加載圖片,用戶就需要等待一段時間才能看到圖片顯示出來,如果把圖片加載時間提前,圖片元素距離可視區域一定范圍內就加載圖片,那么用戶等待時間就會減少一些。
兼容性好,各個環節可以控制。
性能相對不是很好,滾動事件頻繁觸發,并且獲取元素的位置信息,可能會強行觸發重排和重繪導致一定的性能消耗。
大致思路和方案二一樣只是把獲取圖片元素 offsetTop
改成 getBoundingClientRect
方法獲取離可視區頂端的距離。比方案一要簡單一點,缺點也和方案一一樣。
該方案是通過 IntersectionObserver 來判斷圖片元素是否在可視區域內。
IntersectionObserver() 構造器創建并返回一個 IntersectionObserver 對象。如果指定 rootMargin 則會檢查其是否符合語法規定,檢查閾值以確保全部在 0.0 到 1.0 之間,并且閾值列表會按升序排列。如果閾值列表為空,則默認為一個 [0.0] 的數組。 —— MDN
完全沒看懂mdn的這段解釋,簡單說就是
IntersectionObserver 接口提供了一種異步觀察目標元素與祖先元素或頂級文檔 viewport 的交集中的變化的方法。祖先元素與視窗viewport被稱為根(root)。
var images = document.querySelectorAll(".lazyload"); var io = new IntersectionObserver( function (entries) { entries.forEach((item) => { // isIntersecting是一個Boolean值,判斷目標元素當前是否可見 if (item.isIntersecting) { item.target.src = item.target.dataset.src; // 圖片加載后即停止監聽該元素 io.unobserve(item.target); } }); } ); images.forEach(function(image){ io.observe(image) })
IntersectionObserver 的監聽對于頁面局部滾動條也是有效的,不用再單獨對局部滾動條進行處理。 而提前加載圖片通過配置 rootMargin 來擴大監聽區域。
var images = document.querySelectorAll(".lazyload"); var io = new IntersectionObserver( function (entries) { entries.forEach((item) => { // isIntersecting是一個Boolean值,判斷目標元素當前是否可見 if (item.isIntersecting) { item.target.src = item.target.dataset.src; // 圖片加載后即停止監聽該元素 io.unobserve(item.target); } }); }, { root: null, rootMargin: "0px 0px 300px 0px", } ); images.forEach(function(image){ io.observe(image); })
在一些低版本瀏覽器中還存在一些問題。
性能好,簡單。
低版本瀏覽器 IntersectionObserver 存在問題,不支持 IE。
布局抖動是因為開始圖片沒有寬高,內容顯示出來后有了寬高導致位置變動。帶來的影響主要是用戶體驗不好,用戶的注意力已經鎖定了某個區域準備閱讀,突然那個區域下移了,中斷閱讀而重新定位。可以直接在 img 標簽上設置要加載圖片的寬高。
<img src="blank.gif" data-src="normal.jpg" />
解決問題:方案解決的問題范圍是圖片寬高固定的情況,在響應式環境圖片寬高不確定下不適用。
用戶體驗:img的默認占位圖是一個loading或是灰色背景,圖片還沒加載的體驗。
雖然響應式下圖片的寬高會變,但是圖片的寬高比是不變的,圖片的寬高比變了圖片也就變形了。所以 img 標簽設定圖片寬高比,就能根據不同視圖的寬度算出不同高度。 先創建一個寬高比為 5:1 的 div。
<div ></div>
padding 為百分比是相對自身寬度的百分比。 然后再創建了一個寬高比為 5:1 的 img。
<div > <img > </div>
這樣就能適應響應式的寬度改變,這種方式叫 Aspect Ratio Boxes。 占位圖片可以設置成原圖片的小尺寸圖片,被放大后圖片變模糊,這樣開始加載小圖片但圖片的輪廓出現,后面在加載大圖片顯示清晰,給用戶的體驗是圖片開始就在加載,然后加載完成就變清晰了。 img 標簽 srcset 屬性是處理響應式圖片的。懶加載中可以設置 data-srcset 來延遲修改 srcset 屬性。
<img>
標簽中的 src 屬性攜帶的仍然是原始大小的圖片確保了站外 SEO、社會化分享、RSS 等不會讀不到原圖。Aspect Ratio Boxes 方式使占位圖片適應響應式,srcset 屬性存放了一張原圖的小尺寸縮略圖阻止 src 原圖的加載而加載縮略圖優化加載體驗,最后延遲將 data-srcset 的值賦值到 srcset 中。
lazyload.js 是 IntersectionObserver 方式,而且當瀏覽器不支持 IntersectionObserver 的時候就直接加載圖片,沒有延遲加載的功能。
vue-lazyload 使用 IntersectionObserver 和 getBoundingClientRect 方式,默認 getBoundingClientRect 方式懶加載,里面的一些封裝細節有很多有意思的地方,不止綁定了 onscroll 事件還綁定了 'onwheel'、'onmousewheel'、'onresize'、 'onanimationend'、'ontransitionend'、'ontouchmove'問什么要綁定這么多事件,插件為什么默認 getBoundingClientRect 方式而不用 IntersectionObserver 方式,待下回分解。
react-lazyload 只用了 getBoundingClientRect 方式,里面的封裝細節也很有意思。
關于“前端JS圖片懶加載原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。