懒加载的基本思路
当dom元素进入可视区域时,才去加载它
利用浏览器提供的 IntersectionObserver,监听图片元素是否进入可视区域,进入后才真正去设置图片元素的 src
属性进行图片加载
格式:
var dom = dom元素
// 实例化一个观察者
// 它的参数1是一个回调:当被观察的目标进入视口/离开视口就会调用
var observer = new IntersectionObserver((entries)=>{
console.log(entries[0].isIntersecting)
console.log(entries[0].intersectionRatio)
if(entries[0].isIntersecting) {
}
}, 其他配置)
// 观察者观察dom
observer.observe(dom)
observer.disconnect() // 停止观察者
observer.unobserve(dom) // 观察者停止对dom的观察
回调里有一个参数 entries, entries里有 isIntersecting这么一个属性,他一个布尔值 false表示还没有在可视范围内,未加载,反之true表示在可视范围内已加载
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<div style='height:100px'>123123</div>
<button style='height:100px'> 123</button>
<div style='height:100px'>123123</div>
<script>
var btn = document.querySelector('button')
var io = new IntersectionObserver((entries)=>{
console.log(entries);
console.log('按钮要加载了....');
if(entries[0].isIntersecting){
console.log('资源已在可视范围内....加载资源');
//清除监听
io.disconnect()
// io.unobserve()
}
} ,{rootMargin: "100px"})//距离资源100px的时候加载
io.observe(btn)
</script>
进而封装一个Image懒加载组件 传入url 和className
import classnames from 'classnames'
import { useEffect, useRef, useState } from 'react'
import Icon from '../Icon'
import styles from './index.module.scss'
/**
* 拥有懒加载特性的图片组件
* @param {String} props.src 图片地址
* @param {String} props.className 样式类
*/
type Porps = {
src: string
className?: string
}
const Image = ({ src, className }: Porps) => {
// 记录图片加载是否出错的状态
const [isError, setIsError] = useState(false)
// 记录图片是否正在加载的状态
const [isLoading, setIsLoading] = useState(true)
// 对图片元素的引用
const imgRef = useRef<HTMLImageElement>(null)
// 图片懒加载的副作用
useEffect(() => {
const ib = new IntersectionObserver((entries) => {
console.log('entries', entries)
// isIntersecting
if (entries[0].isIntersecting) {
imgRef.current!.src = imgRef.current!.getAttribute('data-src')!
ib.disconnect()
}
})
ib.observe(imgRef.current!)
return () => {
ib.disconnect()
}
}, [])
return (
<div className={classnames(styles.root, className)}>
{/* 正在加载时显示的内容 */}
{isLoading && (
<div className="image-icon">
<Icon type="iconphoto" />
</div>
)}
{/* 加载出错时显示的内容 */}
{isError && (
<div className="image-icon">
<Icon type="iconphoto-fail" />
</div>
)}
{/* 加载成功时显示的内容 */}
{!isError && (
<img
alt=""
data-src={src}
ref={imgRef}
// 图片加载完成事件
onLoad={() => setIsLoading(false)}
// 图片加载失败事件
onError={() => setIsError(true)}
/>
)}
</div>
)
}
export default Image