一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
问题
图片懒加载是一个非常常见的前端优化技巧。其基本逻辑是:
- 图片初始不设置src属性值。这样它就不会请求这个图片资源。
- 实时监听图片是否进入了视口区域,如果进入了,就设置它的src属性,并取消实时监听。
上面的第2点对应的伪代码是:
window.滚动条事件 = () => {
if(图片进入可视区) {
图片.src = 图片地址
取消监听
}
}
代码实现有两个难点:
- 如何判断图片是否进入可视区?
- 如何取消监听?
好了,今天介绍一个老朋友:IntersectionObserver。
IntersectionObserver的格式
// 0. 这是要观察的dom
var dom = dom元素
// 1. 实例化一个观察者
// 参数1: 是一个回调,当被观察的目标进入视口或者离开视口时会被调用
// 参数2:[配置项](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/IntersectionObserver#%E5%8F%82%E6%95%B0)
var observer = new IntersectionObserver((entries)=>{
console.log(entries)
}, 配置)
// 2. 开启观察dom
observer.observe(dom)
// 此时,滚动条的滚动 会触发上边的回调函数。
// 3. 其他辅助api
observer.disconnect() // 停止观察者行为
observer.unobserve(dom) // 停止对dom的观察
图片懒加载的参考demo
下面的代码直接复制就可以跑起来
<!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" 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>
// 1. 找到dom
var img = document.querySelector("img")
// 2. 实例化 IntersectionObserver对象
var observer = new IntersectionObserver(arr=>{
console.log(arr[0].isIntersecting) // 是否进入;false | true
if(arr[0].isIntersecting) {
img.src = img.getAttribute('data-src')
img.onerror = function(){
console.log('图片加载失败....')
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>
效果
初始图片并不可见,快了进入视口区域才可见(注意上面的rootMargin:"100px")
持续改进
- 封装成组件。根据具体的技术栈来做封装。
- 处理异常。 图片加载失败(例如地址错误)
- 优化体验。例如可以提供加载中,加载失败的占位图