一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情。
大家好,我是大帅子,今天给大家讲一下图片的懒加载,众所周知,现在单页面的应用越来越多了,但是首屏加载的速度一直都是一个难题,那么我们该如何解决呢,今天我们就来看一下其中的一种解决方案图片懒加载吧,下面我们直接开始吧,
IntersectionObserver
目标
封装一个拥有懒加载功能的图片组件,实现对文章列表项上的封面图片进行懒加载
懒加载的基本思路
当dom元素进入可视区域时,才去加载它。
如何判断一个dom元素是否进入了可见区域?
- 传统: 获取dom的位置,手动判断。(距离页面顶部的距离 比较 滚动条卷起的高度,还要考虑元素自己的高度 )
- 现在:直接判断元素进入可视区域的比例
实现思路
利用浏览器提供的 IntersectionObserver,监听图片元素是否进入可视区域,进入后才真正去设置图片元素的 src
属性进行图片加载。
格式
var dom = dom元素
// 1. 实例化一个观察者
// 它的参数1是一个回调:当被观察的目标进入视口/离开视口就会调用
var observer = new IntersectionObserver((entries)=>{
console.log(entries[0].isIntersecting)
console.log(entries[0].intersectionRatio)
if(entries[0].isIntersecting) {
}
}, 其他配置)
// 2. 观察者观察dom
observer.observe(dom)
observer.disconnect() // 停止观察者
observer.unobserve(dom) // 观察者停止对dom的观察
示例代码
<!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;">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_search1/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>
var img = document.querySelector("img")
var observer = new IntersectionObserver((arr)=>{
// isIntersecting 判断图片是不是在可视的区域
console.log(arr[0].isIntersecting)
// isIntersecting 判断图片是不是在可视的区域
console.log(arr[0].intersectionRatio)
if(arr[0].isIntersecting) {
img.src = img.getAttribute('data-src')
img.onerror = function(){
img.src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fcdb58e6860bcf06028c4b40e47aa17fd7ffa2e6f67cc-UQZRFK_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641366439&t=07c8d22d1c35def62dcaaf04d94e1255"
}
observer.unobserve(img)
}
}, {rootMargin: "100px"})
observer.observe(img)
</script>
</body>
</html>
上面就是图片懒加载的元祖,我们在平时如果想写一个这个dome的话,这个就是一个不错的选择
图片懒加载组件
import classnames from 'classnames'
import { useEffect, useRef, useState } from 'react'
import Icon from '../Icon'
import styles from './index.module.scss'
type Props = {
src: string
className?: string
}
const Image = ({ src, className }: Props) => {
// 对图片元素的引用
const [isLoading, setIsLoading] = useState(false)
const [isError, setIsError] = useState(false)
const imgRef = useRef<HTMLImageElement>(null)
useEffect(() => {
// 1. 创建 IntersectionObserver对象
const ob = new IntersectionObserver((entry) => {
console.log(entry[0].isIntersecting)
if (entry[0].isIntersecting) {
// 图片可见
if (imgRef.current) {
imgRef.current.src = src
setIsLoading(true) // 浏览器去请求图片了
}
ob.unobserve(imgRef.current!)
}
})
// 2. 观察img
ob.observe(imgRef.current!)
}, [])
return (
<div className={classnames(styles.root, className)}>
{/* 情况1:正在加载时显示的内容 */}
{
isLoading && <div className="image-icon">
<Icon type="iconphoto" />
</div>
}
{/* 情况2:加载出错时显示的内容 */}
{
isError && <div className="image-icon">
<Icon type="iconphoto-fail" />
</div>
}
{/* 情况3:加载成功时显示的内容 */}
{<img
onError={() => { setIsError(true) }}
onLoad={() => { setIsLoading(false); console.log('图片加载完成') }} alt="" data-src={src} ref={imgRef} />}
</div>
)
}
export default Image
样式
.root {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
:global {
img {
display: block;
width: 100%;
height: 100%;
}
.image-icon {
position: absolute;
left: 0;
top: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background-color: #f7f8fa;
}
.icon {
color: #dcdee0;
font-size: 32px;
}
}
}