在一些圖片比較多的網(wǎng)站會(huì)用到 圖片懶加載 技術(shù),這項(xiàng)技術(shù)可以延遲加載圖像荠呐,只當(dāng)圖片出現(xiàn)在我們看到的視圖中才加載挥吵,它的好處是大大提高用戶體驗(yàn),節(jié)省不必要的資源浪費(fèi)以及網(wǎng)站的性能提升等祭椰。下面介紹幾種圖片懶加載的方法臭家,分別是監(jiān)聽(tīng) scroll、resize
事件方淤,使用 Intersection Observer
API 以及 Chrome70 自帶的懶加載設(shè)置钉赁。任何技術(shù)都是為解決問(wèn)題服務(wù)的。在開(kāi)始之前携茂,還是要了解清楚「是什么」以及「為什么」你踩。
什么是懶加載
當(dāng)一個(gè)網(wǎng)站的圖片數(shù)量較多時(shí),直接加載可能會(huì)有很大的開(kāi)銷讳苦,不利于性能带膜,這時(shí)可以將所有的圖片換成輕量的占位圖,不加載圖片鸳谜。而當(dāng)用戶真正滾動(dòng)到圖片出現(xiàn)時(shí)膝藕,再迅速將占位圖片換成真正我們想展示的圖片,這整個(gè)過(guò)程就是懶加載咐扭。
為什么要懶加載
當(dāng)你打開(kāi)一個(gè)網(wǎng)站時(shí)芭挽,瀏覽器會(huì)做許多工作滑废,這其中包括下載各種可能用到的資源,然后渲染呈現(xiàn)在你面前览绿,假設(shè)你的網(wǎng)站有大量的圖片策严,那么加載的過(guò)程是很耗時(shí)的,尤其像那些新聞資訊類需要大量圖片的網(wǎng)站饿敲,可想而知妻导,網(wǎng)站的初始加載時(shí)間會(huì)很長(zhǎng),再加上網(wǎng)絡(luò)等其它影響怀各,用戶體驗(yàn)會(huì)很差倔韭,相信你經(jīng)常遇到過(guò)一個(gè)網(wǎng)站卡在某個(gè)地方,一直在加載瓢对,這種體驗(yàn)很不好寿酌。我們都希望一輸入網(wǎng)址,頁(yè)面立馬就呈現(xiàn)在眼前硕蛹。
既然想要頁(yè)面立馬呈現(xiàn)在面前醇疼,那勢(shì)必要減少瀏覽器的負(fù)荷,優(yōu)化代碼法焰,減少一些不必要的請(qǐng)求和不必要資源的加載秧荆,因?yàn)槟愦蜷_(kāi)網(wǎng)站的時(shí)候,瀏覽器會(huì)把所有可能的資源都下載好埃仪,而實(shí)際上有些資源你并不需要用到乙濒,這就造成了浪費(fèi)。所以有必要在一些資源上做下優(yōu)化卵蛉,提高網(wǎng)站加載速度颁股。
滾動(dòng)事件監(jiān)聽(tīng)
前面說(shuō)到要等圖片出現(xiàn)在視口時(shí)才加載,那么肯定要監(jiān)控瀏覽器的 scroll 事件傻丝,并且要計(jì)算圖片與瀏覽器窗口的距離來(lái)選擇替換圖片的 src 地址甘有。代碼如下:
HTML
<div>
<img class="lazy-load" data-src="https://source.unsplash.com/random/600" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/700" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/800" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/900" alt="">
</div>
// 引入 lodash 庫(kù)
<script src="https://cdn.bootcss.com/lodash.js/4.17.12-pre/lodash.core.min.js"></script>
CSS
div {
margin-top: 350px;
}
.lazy-load {
width: 200px;
height: 150px;
}
JS
let lazyImages = [...document.querySelectorAll('.lazy-load')]
let inAdvance = 300
function lazyLoad() {
lazyImages.forEach(image => {
if (image.offsetTop < window.innerHeight + window.pageYOffset + inAdvance) {
image.src = image.dataset.src; // 替換真實(shí)圖片的 URL
}
})
}
lazyLoad();
window.addEventListener('scroll', _.throttle(lazyLoad, 50))
window.addEventListener('resize', _.throttle(lazyLoad, 50))
這其中有幾個(gè)屬性,首先是 data-src
桑滩,它是自定義屬性梧疲,可以在 js 里通過(guò) dataset
獲得它的屬性值;還有 offsetTop
,innerHeight
以及 pageYOffset
屬性运准,你可以通過(guò) MDN 文檔查詢他們的定義和用法;最后是 _.throttle
函數(shù)缭受,它是一個(gè)節(jié)流函數(shù)胁澳,引用自 lodash
庫(kù),因?yàn)楸O(jiān)聽(tīng) scroll
滾動(dòng)以及 resize
窗口改變事件會(huì)不斷地觸發(fā)米者,過(guò)于頻繁韭畸,所以使用節(jié)流函數(shù)讓其每隔一段時(shí)間執(zhí)行宇智,節(jié)省開(kāi)銷。
Intersection Observer API
現(xiàn)在胰丁,有一個(gè) Intersection observer
接口可以方便我們操作随橘,它可以異步觀察目標(biāo)元素與祖先元素或頂層文件的交集變化。簡(jiǎn)單的說(shuō)锦庸,以前我們需要自己去寫滾動(dòng)監(jiān)聽(tīng)事件函數(shù)机蔗,現(xiàn)在,這個(gè) API 可以幫助我們甘萧,我們只需要統(tǒng)一寫一個(gè) 觀察函數(shù) 萝嘁,每當(dāng)想觀察的元素進(jìn)入視口,也就是我們看見(jiàn)它時(shí)扬卷,就執(zhí)行相應(yīng)的操作牙言。看看以下 js 代碼:
<style> // css 部分
.lazy-load {
width: 400px;
height: 300px;
}
</style>
<div>
<img class="lazy-load" data-src="https://source.unsplash.com/random/600" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/700" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/800" alt="">
<img class="lazy-load" data-src="https://source.unsplash.com/random/900" alt="">
</div>
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [...document.querySelectorAll('.lazy-load')];
if ("IntersectionObserver" in window) {
// 創(chuàng)建一個(gè)觀察函數(shù)怪得,以便待會(huì)調(diào)用
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src; // 替換 src URL
lazyImageObserver.unobserve(lazyImage); // 解除觀察
}
});
});
// 對(duì)所有需要懶加載的圖片進(jìn)行 “暗中觀察”
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
}else{
alert('您的瀏覽器不支持 IntersectionObserver');
}
});
可以看到咱枉,里面監(jiān)聽(tīng)了 DOMContentLoaded
事件,當(dāng)初始的 HTML 文檔被完全加載和解析完成之后徒恋,這個(gè)事件就被觸發(fā)蚕断,在頁(yè)面初始之后獲取到所有圖片元素,然后進(jìn)行觀察因谎。
那既然這個(gè) API 這么好基括,又簡(jiǎn)便易用,有沒(méi)什么缺點(diǎn)呢财岔?相信你看了上面的代碼就能知曉风皿,對(duì),瀏覽器兼容問(wèn)題=宠怠桐款!
可以看到,還是有很多泛紅夷恍,只有 Chrome 支持的最好魔眨,從 58 以上版本就完全支持了,F(xiàn)irefox 也不錯(cuò)酿雪。如果你的項(xiàng)目不需要考慮兼容的話遏暴,可以嘗試使用下它,看看效果指黎。
Chrome 瀏覽器自帶
這個(gè)方法厲害了朋凉,沒(méi)有前面兩種方法那么復(fù)雜,它是 Chrome 自帶的原生 lazyload 屬性醋安,只需要一個(gè)開(kāi)關(guān)杂彭。
復(fù)制它到 Chrome 瀏覽器的地址欄墓毒,然后找到如下選項(xiàng),將其設(shè)置為「Enabled」亲怠。
然后在 HTML 標(biāo)簽里開(kāi)啟:
<img src="https://source.unsplash.com/random/600" alt="" lazyload="on">
不需要多余的代碼所计,不需要 JS ,簡(jiǎn)直強(qiáng)大团秽。
比較三者
秉承著嘗試搗鼓新技術(shù)的原則主胧,應(yīng)該優(yōu)先使用 Intersection Observer
,隨著越來(lái)越多的瀏覽器支持會(huì)更廣泛地應(yīng)用;但如果要考慮瀏覽器的兼容問(wèn)題徙垫,那就要使用平常的 scroll,resize
事件監(jiān)聽(tīng)了讥裤,配合 offsetTop 、innerHeight 以及 pageYOffset 幾個(gè)屬性實(shí)現(xiàn)姻报。至于最簡(jiǎn)單粗暴的那個(gè)方法己英,很明顯只能在特定的 Chrome 70 以上版本中使用,有很大的局限性吴旋,不過(guò)現(xiàn)在使用 Chrome 的人非常的多损肛,所以也是有用處的。
綜合來(lái)看荣瑟,應(yīng)該將 Intersection Observer
和 scroll,resize
結(jié)合起來(lái)使用治拿,這可能是最優(yōu)也最兼容的方案。
參考資料
[1] MDN 文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API
[2] 延遲加載圖像和視頻:https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/
[3] Lazy load images:https://medium.com/@filipvitas/lazy-load-images-with-zero-javascript-2c5bcb691274