图片懒加载
1.懒加载是什么
图片进入到可视区发起网络请求获取图片。
1.1为什么要懒加载
懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。
1.2懒加载的实现原理?
一张图片就是一个标签,而图片的来源主要是src属性。浏览器是否发起亲求就是根据是否有src属性决定的。 既然这样,那么我们就要对标签的src属性下手了,在没进入可视区域的时候,我们先不给这个标签赋src属性,这样岂不是浏览器就不会发送请求了。
2.如何判断图片是否进入了可视区
元素到可视区顶部的距离小于可视区的高度
元素到可视区顶部的距离等于元素到浏览器顶部的距离-滚动条的距离或者使用getBoundingClientRect()
方法获取
2.1第一种实现方式
获取传递过来的图片地址,通过html5的自定义属性,将src赋值到data-src上。 通过监听滚动获取元素的位置,如果图片出现在可视区,获取到img元素的data-src属性,赋值到src上。
import React from "react";
const Index = (props) => {
const { src } = props;
const imgRef = React.useRef();
React.useEffect(() => {
const { current } = imgRef;
const isVisable = () => {
const { top } = current.getBoundingClientRect();
if (top <= window.innerHeight) {
current.src = current.getAttribute("data-src");
}
};
isVisable();
window.addEventListener("scroll", function () {
isVisable();
});
}, []);
return <img ref={imgRef} src="" alt="" data-src={src} width="100%" height="100%" />;
};
export default Index;
2.2第二种实现方式
传统的实现方法是,监听到scroll事件后,调用目标元素(绿色方块)的 getBoundingClientRect()
方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件密集发生,计算量很大,容易造成性能问题。
目前有一个新的 IntersectionObserver API
,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。
IntersectionObserver
对象提供目标元素的信息,一共有六个属性。
{
time: 3893.92,
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
boundingClientRect: ClientRect {
// ...
},
intersectionRect: ClientRect {
// ...
},
intersectionRatio: 0.54,
target: element
}
每个属性的含义如下。
time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
target:被观察的目标元素,是一个 DOM 节点对象
rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
boundingClientRect:目标元素的矩形区域的信息
intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
import React from "react";
const Index = (props) => {
const { src } = props;
const imgRef = React.useRef();
React.useEffect(() => {
const { current } = imgRef;
const io = new IntersectionObserver(entries=>{
entries.forEach(item=>{
if (item.intersectionRatio <= 0) return;
current.src = current.getAttribute("data-src");
io.unobserve(current)
})
})
io.observe(current)
}, []);
return <img ref={imgRef} src="" alt="" data-src={src} width="100%" height="100%" />;
};
export default Index;