利用浏览器提供的 IntersectionObserver,监听图片元素是否进入可视区域,进入后才真正去设置图片元素的 src
属性进行图片加载。
详细请看阮一峰IntersectionObserver API 使用教程
src
属性进行图片加载。var dom = dom元素 // 实例化一个观察者 // 它的参数1是一个回调:当被观察的目标进入视口/离开视口就会调用 var observer = new IntersectionObserver( entries => { console.log(entries); }, 其他配置) // 观察者观察dom observer.observe(dom) observer.disconnect() // 停止观察者 observer.unobserve(dom) // 观察者停止对dom的观察
callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <p style="padding: 30px;">1</p> <p style="padding: 30px;">2</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">4</p> <p style="padding: 30px;">5</p> <p style="padding: 30px;">6</p> <p style="padding: 30px;">7</p> <p style="padding: 30px;">8</p> <p style="padding: 30px;">9</p> <!-- <img height="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ffzn.cc%2Fwp-content%2Fuploads%2F2020%2F04%2F640-8.jpg&refer=http%3A%2F%2Ffzn.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641365183&t=b5d7bdae0fe3f2c4831b52e3985abdf1" /> --> <img height="200" data-src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ffzn.cc%2Fwp-content%2Fuploads%2F2020%2F04%2F640-8.jpg&refer=http%3A%2F%2Ffzn.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641365183&t=b5d7bdae0fe3f2c4831b52e3985abdf1" /> <p style="padding: 30px;">1</p> <p style="padding: 30px;">2</p> <p style="padding: 30px;">3</p> <p style="padding: 30px;">4</p> <p style="padding: 30px;">5</p> <p style="padding: 30px;">6</p> <p style="padding: 30px;">7</p> <p style="padding: 30px;">8</p> <p style="padding: 30px;">9</p> </div> <script> // 获取DOM元素 var img = document.querySelector("img") // 实例化 IntersectionObserver var io = new IntersectionObserver(entries => { // 当被观察的目标进入视口/离开视口就会调用 if (entries[0].isIntersecting) {//当前DOM是否进入到视口 布尔值 console.log(img.getAttribute('data-src')); // 当前DOM进入到视口将自定义属性值给src img.src = img.getAttribute('data-src') img.onerror = function () { // 图片加载失败会被DOM的onerror方法捕获到 console.log('图片加载失败'); } console.log(img.src); // 停止观察者 io.disconnect() } }, { rootMargin: '100px' }); //DOM四周偏移100px (提前100px触发回调) // 观察者观察dom io.observe(img) </script> </body> </html>
以下是搭配TS些的如果是js就之前删除类型即可 (type,<HTMLImageElement>…这一类)
scr/components/Image.tsx
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 Props = { src: string className?: string alt?:string } const Image = ({ src, className, alt }: Props) => { // 封装Image懒加载图片组件 const [upload, setUpload] = useState(true) // 加载中的变量等加载完后变成false const [lose, setLose] = useState(true) // 加载失败的变量 // 对图片元素的引用 const imgRef = useRef<HTMLImageElement>(null) useEffect(() => { // 副作用 const ob = new IntersectionObserver((entries) => { // 实例化IntersectionObserver if (entries[0].isIntersecting) { // 当图片进入到视图 imgRef.current!.src = imgRef.current?.getAttribute('data-src')! ob.unobserve(imgRef.current!) // 观察者停止对dom的观察 } }, { rootMargin: '100px' })// 让图片提前100px加载 ob.observe(imgRef.current!) // 观察者观察dom return () => { ob.disconnect() // 停止观察者 } // 当组件销毁时停止观察者 }, []) return ( <div className={classnames(styles.root, className)}> {/* 正在加载时显示的内容 */} { upload && <div className="image-icon"> <Icon type="iconphoto" /> </div> } {/* 加载出错时显示的内容 */} { !lose && <div className="image-icon"> <Icon type="iconphoto-fail" /> </div> } {/* 加载成功时显示的内容 */} {<img onl oad={() => { setUpload(false) }} // 图片加载完执行 one rror={() => setLose(false)} // 图片获取失败执行 alt={alt} data-src={src} ref={imgRef} /> } </div> ) } export default Image