背景
在一些图片资源比较丰富的场景下,如果没有被用户观察到的图片也直接去加载的话,页面往往会变得十分卡顿,像下面这样,一次性加载上千上图片,显而易见的卡顿会给用户极其不好的体验。
因此我们需要通过懒加载,也称为延迟加载,在图片滚动到用户可视区域内一定范围的时候去加载,从而有效提高用户体验、减少无效资源的请求。
实现方案
实现逻辑也很简单
1、将图片真实的url挂载到img元素的自定义属性上data-xxx,url为空或者给定一个预设图片。
2、监听图片是否出现在用户的可视区域内。
3、出现在可视区域内后更新url属性为真实图片地址。
第一步和第三步较为常规,不加以赘述。
第二步的传统实现方案,大多数是以下步骤
-
找到列表容器
-
监听scroll事件,对比容器的高度、滚动高度、和图片距离容器顶部的高度,判断是否滚动到可视区域
-
更新图片真实src
期间涉及到比较繁琐的dom元素属性计算以及节流等操作,一分钟不太好实现,不太契合文章标题。
因此,引入一下文章的主角:IntersectionObserver(话说MDN的新UI还没看习惯)
IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。
换句话说,我们可以通过它来实现“观察”图片是否出现可视区域内。
对于每张图片
//... itemRef挂载到单张图片元素上
const cb = (entrys) => {
if (!itemRef.current) return
// enterys是实例观察的对象数组,其中target为对应的的dom元素,isIntersecting为交叉状态,即是否出现在了指定的可视区域内。
const { isIntersecting, target } = entrys[0]
if (isIntersecting) {
// 出现在可视区域内后,替换真实src,
target.src = src
// 停止对该元素的观察。
observerRef.current.unobserve(itemRef.current)
}
}
useLayoutEffect(() => {
const options = {
// 容器元素
root: document.querySelector('#upload_container'),
// 图片出现在容器元素可视区域上下100px内均视为图片出现
rootMargin: '100px',
}
// 生成一个IntersectionObserver实例监听图片和容器的交叉状态。
observerRef.current = new IntersectionObserver(cb, options)
if (itemRef.current) {
observerRef.current.observe(itemRef.current)
}
}, [])
这样就完美的实现的图片懒加载,告别计算dom的各种属性值~