前端必會(huì)的圖片懶加載(三種方式)
一.何為懶加載🌃:
在我們?cè)L問(wèn)一個(gè)圖片展示比較多的網(wǎng)頁(yè)時(shí),加載速度慢很多時(shí)候正是因?yàn)閳D片多導(dǎo)致,大量的img圖片導(dǎo)致頁(yè)面渲染的堵塞。當(dāng)費(fèi)了許多力氣把全部圖片和頁(yè)面加載出來(lái)時(shí)而用戶早已離去。另一方面,若用戶只查看了網(wǎng)頁(yè)的前面部分便離開(kāi),許多已經(jīng)加載卻因?yàn)樘幱诰W(wǎng)頁(yè)底部而未呈現(xiàn)在視口區(qū)的圖片,它們極大加重服務(wù)器壓力了但是用戶看都沒(méi)看,白白浪費(fèi)了性能。
為了解決上面的問(wèn)題需要引入圖片懶加載,懶加載其實(shí)很好理解,重點(diǎn)就是一個(gè)‘懶'字。當(dāng)用戶滾動(dòng)相應(yīng)可視區(qū)域,若可視區(qū)域有圖片便加載,而在可視區(qū)域外未加載過(guò)的圖片它們先不加載,如果用戶滾動(dòng)可視區(qū)域到它們時(shí)它們?cè)偌虞d,否則一律不加載。這樣一來(lái)就大大提高了網(wǎng)頁(yè)渲染的性能和減少不必要的浪費(fèi)。
二.實(shí)現(xiàn)懶加載🌄:
首先,先定義一個(gè)基本的HTML頁(yè)面模擬一些存在大量圖片的網(wǎng)頁(yè),比如我用8個(gè)img 標(biāo)簽來(lái)模擬,同時(shí)定義一些基本css樣式,代碼與初始效果如下:
html:
<img src="img/1.jpg" alt="xxx" /> <img src="img/2.jpg" alt="xxx" /> <img src="img/3.jpg" alt="xxx" /> <img src="img/4.jpg" alt="xxx" /> <img src="img/5.jpg" alt="xxx" /> <img src="img/6.jpg" alt="xxx" /> <img src="img/7.jpg" alt="xxx" /> <img src="img/8.jpg" alt="xxx" />
css:
* { margin: 0; padding: 0; box-sizing: border-box; } img { width: 500px; height: 300px; object-fit: cover; margin: 20px; } body { display: flex; flex-wrap: wrap; justify-content: space-evenly; }
初始效果如下,可以看到右邊的控制臺(tái),8張圖片在我一運(yùn)行這個(gè)頁(yè)面的時(shí)候就都一同被加載渲染了:
下面是利用JavaScript實(shí)現(xiàn)懶加載的3種方式,原理都是判斷圖片是否出現(xiàn)在可視區(qū)后給圖片賦值src屬性。
2.1 第一種方式:
首先,修改每一個(gè)img標(biāo)簽,利用HTML提供的 data- 屬性來(lái)嵌入自定義數(shù)據(jù),這個(gè)自定義數(shù)據(jù)我們存放這個(gè)標(biāo)簽原本的圖片地址。同時(shí),全部的圖片的src屬性我們都用一張同樣的圖表示,這個(gè)圖一般可為顯示載入中字樣的圖片。注意,如果多個(gè)img標(biāo)簽src引用的是同一張圖片,那么只會(huì)加載一次,不會(huì)多次加載,所以我下面給每個(gè)圖src定義同一張圖。
<img data-src="img/1.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/2.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/3.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/4.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/5.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/6.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/7.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/8.jpg" src="img/0.png" alt="xxx" />
此時(shí)頁(yè)面效果如下,:
接下來(lái)用JavaScript實(shí)現(xiàn)當(dāng)我們滾動(dòng)滾動(dòng)條時(shí),如果圖片出現(xiàn)在可視區(qū),那么加載圖片。加載圖片其實(shí)就是給img標(biāo)簽src屬性賦值為本來(lái)的地址,那么此時(shí)圖片便會(huì)請(qǐng)求加載渲染出來(lái)。
//獲取全部img標(biāo)簽 var images = document.getElementsByTagName("img"); window.addEventListener("scroll", (e) => { //當(dāng)發(fā)生滾動(dòng)事件時(shí)調(diào)用ergodic事件 ergodic(); }); function ergodic() { // 遍歷每一張圖 for (let i of images) { //判斷當(dāng)前圖片是否在可視區(qū)內(nèi) if (i.offsetTop <= window.innerHeight + window.scrollY) { //獲取自定義data-src屬性的值 let trueSrc = i.getAttribute("data-src"); //把值賦值給圖片的src屬性 i.setAttribute("src", trueSrc); } } } //沒(méi)發(fā)生滾動(dòng)事件時(shí)也要先執(zhí)行一次 ergodic();
其中, offsetTop 為元素距離頂部的距離;window.innerHeight 為當(dāng)前窗口的高度;window.scrollY 為滾動(dòng)距離;不難知道,當(dāng) i.offsetTop <= window.innerHeight + window.scrollY時(shí)圖片就處于窗口可視區(qū)了。
此時(shí)效果如下,觀察右側(cè)控制臺(tái),發(fā)現(xiàn)當(dāng)滾動(dòng)時(shí)圖片才加載:
2.2 第二種方式:
第二種方式其實(shí)和第一種差不多,只是計(jì)算圖片是否在可視區(qū)方式不同,重復(fù)的部分就省略了,如下:
window.addEventListener("scroll", (e) => { ergodic(); }); function ergodic() { for (let i of images) { //計(jì)算方式和第一種方式不同 if (i.getBoundingClientRect().top < window.innerHeight) { let trueSrc = i.getAttribute("data-src"); i.setAttribute("src", trueSrc); } } } ergodic();
其中,getBoundingClientRect().top 為元素相對(duì)于窗口的位置;window.innerHeight 為當(dāng)前窗口的高度;當(dāng)元素對(duì)于窗口的位置小于當(dāng)前窗口的高度時(shí),那自然處于了窗口可視區(qū)了。
效果一樣的:
2.3 第三種方式(優(yōu)):
其實(shí)上面兩種方式已經(jīng)大致實(shí)現(xiàn)懶加載,但是,它們都有一個(gè)缺點(diǎn),就是一當(dāng)發(fā)生滾動(dòng)事件時(shí),就發(fā)生了大量的循環(huán)和判斷操作判斷圖片是否可視區(qū)里。這自然是不太好的,那是否有解決方法。這里就引入了一個(gè)叫 Intersection Observer 觀察器接口,它是是瀏覽器原生提供的構(gòu)造函數(shù),使用它能省到大量的循環(huán)和判斷。當(dāng)然它的兼容可能不太好,看情況使用。
Intersection Observer 是什么呢?這個(gè)構(gòu)造函數(shù)的作用是它能夠觀察可視窗口與目標(biāo)元素產(chǎn)生的交叉區(qū)域。簡(jiǎn)單來(lái)說(shuō)就是當(dāng)用它觀察我們的圖片時(shí),當(dāng)圖片出現(xiàn)或者消失在可視窗口,它都能知道并且會(huì)執(zhí)行一個(gè)特殊的回調(diào)函數(shù),我們就利用這個(gè)回調(diào)函數(shù)實(shí)現(xiàn)我們的操作。概念枯燥難懂,直接看下面例子:
1.既然IntersectionObserver是瀏覽器原生提供的構(gòu)造函數(shù),先new一個(gè)實(shí)例:
const observer = new IntersectionObserver(callback);
其中它會(huì)有一個(gè)參數(shù)callback,參數(shù)為一個(gè)回調(diào)函數(shù),當(dāng)目標(biāo)元素能看見(jiàn)會(huì)觸發(fā)一次,目標(biāo)元素看不見(jiàn)會(huì)再觸發(fā)一次。
2.使用實(shí)例通過(guò)observer屬性可以為每一張圖片綁定一個(gè)觀察器:
for (let i of images) { observer.observe(i); }
3.由上可以知道每張圖片能看見(jiàn)會(huì)和看不見(jiàn)時(shí)都會(huì)觸發(fā)一次callback回調(diào)函數(shù),同時(shí)callback這個(gè)回調(diào)函數(shù)也有一個(gè)參數(shù)entries,我們可以運(yùn)行觸發(fā)這個(gè)回調(diào)函數(shù)看看它是什么:
function callback(entries) { console.log(entries) }
可以看到每次圖片能看見(jiàn)會(huì)和看不見(jiàn)時(shí)都會(huì)觸發(fā)一次callback回調(diào)函數(shù),并輸出了參數(shù)entries的內(nèi)容。其實(shí),entries為一個(gè)數(shù)組,而它的數(shù)組元素為當(dāng)前改變了狀態(tài)觸發(fā)了事件的目標(biāo)元素。其中有一個(gè)isIntersecting屬性,當(dāng)目標(biāo)元素在視口看得見(jiàn)為它true,不在時(shí)它為 false 。我們就可以利用這個(gè)屬性,當(dāng)它為 true 時(shí)設(shè)置觸發(fā)這個(gè)事件的圖片的src屬性值為data-src,開(kāi)始加載。
function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); } } }
其中 target 事件屬性返回觸發(fā)事件的元素。當(dāng)前,當(dāng)回來(lái)滾動(dòng)時(shí),圖片會(huì)一會(huì)可見(jiàn)一會(huì)不可見(jiàn),它都是觸發(fā)回調(diào)函數(shù),所以當(dāng)某圖片已經(jīng)加載時(shí)我們要停掉它的觀察器。利用unobserve屬性可停掉。
function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); // 結(jié)束觀察 observer.unobserve(img); } } }
完整代碼:
var images = document.getElementsByTagName("img"); function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); observer.unobserve(img); } } } const observer = new IntersectionObserver(callback); for (let i of images) { observer.observe(i); }
效果如下,實(shí)現(xiàn)懶加載:
三.總結(jié):
以上就為實(shí)現(xiàn)圖片懶加載的全部?jī)?nèi)容啦。✨總的來(lái)說(shuō)就是某些存在大量的img的網(wǎng)頁(yè)如果頁(yè)面渲染階段就把圖片全部加載,那會(huì)導(dǎo)致頁(yè)面渲染的堵塞。為了解決引入圖片懶加載,用戶滾動(dòng)相應(yīng)可視區(qū)域,若可視區(qū)域有圖片那么該圖片才加載。其中核心就是給圖片定義自定義屬性data-src存放圖片真的訪問(wèn)地址,當(dāng)圖片出現(xiàn)在可視區(qū)時(shí)才將data-src的值賦值給src屬性,此時(shí)圖片便加載。其中不止不可通過(guò)一些常見(jiàn)屬性判斷圖片位置,還引入了IntersectionObserver觀察器接口更少消耗的實(shí)現(xiàn)懶加載。
到此這篇關(guān)于前端必會(huì)的圖片懶加載(三種方式)的文章就介紹到這了,更多相關(guān)圖片懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
2019 年編寫現(xiàn)代 JavaScript 代碼的5個(gè)小技巧(小結(jié))
這篇文章主要介紹了2019 年編寫現(xiàn)代 JavaScript 代碼的5個(gè)小技巧,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01詳談js對(duì)url進(jìn)行編碼和解碼(三種方式的區(qū)別)
下面小編就為大家?guī)?lái)一篇詳談js對(duì)url進(jìn)行編碼和解碼(三種方式的區(qū)別)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08javascript實(shí)現(xiàn)簡(jiǎn)單搜索功能
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)簡(jiǎn)單搜索功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03js 獲取class的元素的方法 以及創(chuàng)建方法getElementsByClassName
js 獲取class的元素的方法 以及創(chuàng)建方法getElementsByClassName,需要的朋友可以參考一下2013-03-03js寫一個(gè)彈出層并鎖屏效果實(shí)現(xiàn)代碼
js實(shí)現(xiàn)一個(gè)彈出層并鎖屏效果是每一網(wǎng)友所期望的效果,于是搜集整理一番,把代碼曬出來(lái)和大家分享2012-12-12JavaScript錯(cuò)誤處理機(jī)制全面分析講解
下面小編就為大家?guī)?lái)一篇全面了解javascript中的錯(cuò)誤處理機(jī)制。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2022-10-10javascript實(shí)現(xiàn)div浮動(dòng)在網(wǎng)頁(yè)最頂上并帶關(guān)閉按鈕效果實(shí)例
我們有時(shí)會(huì)看到有些網(wǎng)站最頂部一直會(huì)跟著我們滾動(dòng)而滾動(dòng)了,這種方法其實(shí)很簡(jiǎn)單,下面我來(lái)給大推薦一個(gè)javascript實(shí)現(xiàn)div浮動(dòng)在網(wǎng)頁(yè)最頂上并帶關(guān)閉按鈕效果2013-08-08