React實(shí)現(xiàn)圖片懶加載的常見方式
前言
圖片懶加載是一種優(yōu)化網(wǎng)頁性能的技術(shù),它允許在用戶滾動(dòng)到圖片位置之前延遲加載圖片。通過懶加載,可以在用戶需要查看圖片時(shí)才加載圖片,避免了不必要的圖片加載,從而提高了網(wǎng)頁的加載速度和用戶體驗(yàn)。
方案一
實(shí)現(xiàn)思路
在說明思路之前,先了解幾個(gè)常見的視圖屬性。
- clientHeight:可視區(qū)域的高度,對(duì)應(yīng)的也就是下圖中的滾動(dòng)區(qū)域。
- scrollTop:滾動(dòng)條滾動(dòng)的高度,它指的是內(nèi)容區(qū)的頂部到可視區(qū)域頂部的距離。
- offsetTop:元素到offsetParent頂部的距離。
- offsetParent:距離元素最近的一個(gè)具有定位的祖宗元素(relative,absolute,fixed),若祖宗都不符合條件,offsetParent為body。
圖解:

根據(jù)上面的圖解可知,當(dāng)圖片的滾動(dòng)條滾動(dòng)的高度加上可視區(qū)域的高度大于當(dāng)前的圖片的offsetTop,那么說明圖片正在進(jìn)入可視區(qū)域。這個(gè)時(shí)候便可以加載當(dāng)前圖片。
第一步
模擬后臺(tái)返回的圖片url,遍歷產(chǎn)生一個(gè)url集合,用于后面的懶加載使用。
const imgUrls = (num = 10) => {
const urls = [];
for (let i = 0; i < num; i++) {
const url = `https://robohash.org/${i}.png`;
urls.push(url);
}
return urls;
};
第二步
遍歷圖片url集合,渲染100張loading圖片,css部分省略。
<div className={styles['box-one']} ref={scrollRef}>
{imgUrls(100).map((item) => {
return <img data-src={item} key={item} src={loadingUrl} alt="" />;
})}
</div>
效果預(yù)覽

第三步
監(jiān)聽容器的滾動(dòng)事件,當(dāng)容器滾動(dòng)時(shí)計(jì)算容器的高度加上滾動(dòng)條的高度大于當(dāng)前圖片的offsetTop時(shí)加載當(dāng)前的圖片。完整代碼如下:
import loadingUrl from '@/assets/imgs/loading.jpg';
import { useEffect, useRef } from 'react';
import styles from '../index.less';
// 圖片url
const imgUrls = (num = 10) => {
const urls = [];
for (let i = 0; i < num; i++) {
const url = `https://robohash.org/${i}.png`;
urls.push(url);
}
return urls;
};
const LazyLoading = () => {
const scrollRef = useRef({} as any);
// 滾動(dòng)事件
const changeScroll = () => {
const clientHeight = scrollRef?.current.clientHeight; //可視區(qū)域高度
const scrollTop = scrollRef?.current.scrollTop; //滾動(dòng)條滾動(dòng)高度
const childNodes = scrollRef?.current.childNodes; // 獲取所有圖片集合
for (let j = 0; j < childNodes.length; j++) {
const element = childNodes[j];
if (scrollTop + clientHeight > element.offsetTop) {
element.src = element.getAttribute('data-src'); // 替換當(dāng)前的src
}
}
};
useEffect(() => {
changeScroll(); // 第一次渲染的時(shí)候替換loading圖片
}, []);
return (
<div className={styles['box-one']} ref={scrollRef} onScroll={changeScroll}>
{imgUrls(100).map((item) => {
return <img data-src={item} key={item} src={loadingUrl} alt="" />;
})}
</div>
);
};
export default LazyLoading;
效果預(yù)覽

方案二
實(shí)現(xiàn)思路
方案二的實(shí)現(xiàn)思路利用瀏覽器提供的 IntersectionObserver API實(shí)現(xiàn)。IntersectionObserver API提供了一種方便的方式來監(jiān)視目標(biāo)元素和其祖先元素或視窗之間的交叉狀態(tài)變化。當(dāng)目標(biāo)元素進(jìn)入或離開視口時(shí),可以觸發(fā)回調(diào)函數(shù),進(jìn)行相應(yīng)的操作。它的原理是通過注冊(cè)一個(gè)回調(diào)函數(shù)來觀察特定元素的交叉狀態(tài)變化,并在滿足條件時(shí)執(zhí)行相應(yīng)的操作。
使用 IntersectionObserver API非常簡(jiǎn)單,可以通過創(chuàng)建一個(gè) IntersectionObserver 實(shí)例,并傳入回調(diào)函數(shù)和選項(xiàng)對(duì)象來實(shí)現(xiàn)?;卣{(diào)函數(shù)會(huì)在目標(biāo)元素的交叉狀態(tài)發(fā)生變化時(shí)被調(diào)用,并接收一個(gè)參數(shù),包含有關(guān)交叉狀態(tài)的信息。
實(shí)現(xiàn)完整代碼
import loadingUrl from '@/assets/imgs/loading.jpg';
import styles from '../index.less';
import React, { useRef, useEffect, useState } from 'react';
// 圖片url
const imgUrls = (num = 10) => {
const urls = [];
for (let i = 0; i < num; i++) {
const url = `https://robohash.org/${i}.png`;
urls.push(url);
}
return urls;
};
const LazyLoadImage = ({ src, alt }) => {
const [imageSrc, setImageSrc] = useState(loadingUrl);
const imgRef = useRef(null as any);
useEffect(() => {
let observer: IntersectionObserver;
if (imgRef.current) {
// 創(chuàng)建IntersectionObserver實(shí)例
observer = new IntersectionObserver(
([entry]) => {
// 當(dāng)圖片進(jìn)入可視區(qū)域時(shí),設(shè)置圖片地址進(jìn)行加載
if (entry.isIntersecting) {
setImageSrc(src);
observer.unobserve(imgRef.current);
}
},
{
rootMargin: '0px 0px 200px 0px', // 可視區(qū)域的上邊距設(shè)置為200px
},
);
observer.observe(imgRef.current); //開始觀察目標(biāo)元素
}
return () => {
if (observer && observer.unobserve) {
observer.unobserve(imgRef.current);
}
};
}, [src]);
return <img ref={imgRef} src={imageSrc} alt={alt} />;
};
const LazyLoading = () => {
return (
<div className={styles['box-two']}>
{imgUrls(100).map((item) => {
return <LazyLoadImage src={item} alt="lazy load image" />;
})}
</div>
);
};
export default LazyLoading;
實(shí)現(xiàn)效果

注意事項(xiàng)
在初始化的時(shí)候,需要給imageSrc設(shè)置一個(gè)初始化的loading地址,如果沒有的話,初始化的時(shí)候會(huì)加載多張圖片。
方案三
實(shí)現(xiàn)思路
利用react的懶加載庫react-lazyload,在使用之前需要先安裝 yarn add react-lazyload,這里介紹幾個(gè)它的常見屬性:
- scrollContainer: 指定的滾動(dòng)的區(qū)域,默認(rèn)值是undefined,如果沒有指定默認(rèn)是窗口的視圖作為滾動(dòng)區(qū)域。
- offset: 元素距離視口頂部的距離,當(dāng)達(dá)到這個(gè)距離時(shí),元素將被加載。
- scroll: 是否監(jiān)聽滾動(dòng)
- height: 渲染元素的占位符的高度。
- overflow : 如果溢出容器,延遲加載組件
代碼實(shí)現(xiàn)
因?yàn)檫@里實(shí)現(xiàn)的圖片懶加載是局部懶加載,所以需要指定 scrollContainer,scrollContainer 的值DOM對(duì)象。在實(shí)現(xiàn)的過程中,同時(shí)需要設(shè)置overflow為true,以及height的值。
import react, { useRef, useEffect } from 'react';
import LazyLoad from 'react-lazyload';
import styles from '../index.less';
// 圖片url
const imgUrls = (num = 10) => {
const urls = [];
for (let i = 0; i < num; i++) {
const url = `https://robohash.org/${i}.png`;
urls.push(url);
}
return urls;
};
const LazyLoading = () => {
const scrollRef = useRef({} as any);
return (
<div className={styles['box-three']} ref={scrollRef}>
{imgUrls(100).map((item) => {
return (
<LazyLoad
height={200}
overflow={true}
offset={0}
key={item}
scroll={true}
scrollContainer={scrollRef.current} // DOM
>
<img src={item} alt="" />
</LazyLoad>
);
})}
</div>
);
};
export default LazyLoading;
實(shí)現(xiàn)效果

以上就是React實(shí)現(xiàn)圖片懶加載的常見方式的詳細(xì)內(nèi)容,更多關(guān)于React圖片懶加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
插件化機(jī)制優(yōu)雅封裝你的hook請(qǐng)求使用方式
這篇文章主要為大家介紹了插件化機(jī)制優(yōu)雅封裝你的hook請(qǐng)求使用方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
React實(shí)現(xiàn)虛擬滾動(dòng)的三種思路詳解
在??web??開發(fā)的過程中,或多或少都會(huì)遇到大列表渲染的場(chǎng)景,為了解決大列表造成的渲染壓力,便出現(xiàn)了虛擬滾動(dòng)技術(shù),本文主要介紹虛擬滾動(dòng)的三種思路,希望對(duì)大家有所幫助2024-04-04
react-redux及redux狀態(tài)管理工具使用詳解
Redux是為javascript應(yīng)用程序提供一個(gè)狀態(tài)管理工具集中的管理react中多個(gè)組件的狀態(tài)redux是專門作狀態(tài)管理的js庫(不是react插件庫可以用在其他js框架中例如vue,但是基本用在react中),這篇文章主要介紹了react-redux及redux狀態(tài)管理工具使用詳解,需要的朋友可以參考下2023-01-01
ReactNative頁面跳轉(zhuǎn)Navigator實(shí)現(xiàn)的示例代碼
本篇文章主要介紹了ReactNative頁面跳轉(zhuǎn)Navigator實(shí)現(xiàn)的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
React使用Context與router實(shí)現(xiàn)權(quán)限路由詳細(xì)介紹
這篇文章主要介紹了React使用Context與router實(shí)現(xiàn)權(quán)限路由的詳細(xì)過程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01
React工作流程及Error Boundaries實(shí)現(xiàn)過程講解
這篇文章主要介紹了React工作流程及Error Boundaries實(shí)現(xiàn)過程講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-02-02
React Native中的RefreshContorl下拉刷新使用
本篇文章主要介紹了React Native中的RefreshContorl下拉刷新使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
React觸發(fā)render的實(shí)現(xiàn)方法
這篇文章主要介紹了React觸發(fā)render的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10

