昨天我写了Vue2 中自定义图片懒加载指令这篇博客,文章数据很好,阅读量可以上千,对于我这个刚写博客一周的新博主来说,是何等的荣幸。
那现在就来更新今天的文章吧,继续延续昨天的文章,昨天的文章有朋友在评论区推荐了Intersection Observer API
来实现图片懒加载,那这篇博客我先介绍一下这 API,但这 API 兼容性一般,且完全不兼容 IE,大家在实际项目中谨慎使用。
但在介绍Intersection Observer API之前,首先要先了解另外三个知识点,分别是 IntersectionObserver()构造器,IntersectionObserverEntry 对象与IntersectionObserverEntry 对象。
他们之前的关系比较复杂,大家可以先看看这个整体关系图,以及他们的参数、属性与方法。
Intersection Observer API
提供了一种异步检测目标元素与祖先元素或视口(可统称为根元素)相交情况变化的方法。
IntersectionObserver API
是通过requestIdleCallback()实现,即只有浏览器空闲下来,才会执行观察器。这意味着这个观察器的优先级非常低。因为在如今网页开发的过程中,常常需要判断某个元素是否进入了"视口"(viewport),即用户能不能看到它。
面对这种相交检测的任务时,过去我们通常会使用Element.getBoundingClientRect()
等方法来获取相关元素的位置信息,并且还会用到事件监听。
然而事件监听和调用Element.getBoundingClientRect()
等 API 都是运行在主线程,因此频繁触发、调用会造成性能问题,而且这种检测方法使用起来比较繁琐。
因此官方就提出了Intersection Observer API
,该 API 的出现就是为了高效的解决以下两大类问题:
Intersection Observer API
会注册一个回调函数,只会在以下两种情况触发:
这样,浏览器的主线程就不用在监听元素是否相交,并且IntersectionObserver API
是异步进行检测的,也不会占用主线程的资源,从而性能上得到了提升。
Intersection observer API 有以下五个重要的概念:
IntersectionObserver()
构造器用于创建一个 IntersectionObserver 对象,并会将该对像进行放回。 该构造器函数的配置参数如下图所示:
其语法如下:
var observer = new IntersectionObserver(callback[, options]);
首先我们先了解一下IntersectionObserver()
构造器的参数,其参数有:
entries
--- 由IntersectionObserverEntry
对象组成的数组 但每个被触发的阈值,都或多或少与指定阈值有偏差。observer
--- 返回被调用的IntersectionObserver
实例。root
--- 指定根元素。用于检查目标的可见性。默认为浏览器视口。 rootMargin
--- 根元素的扩缩边距。其传值形式与 CSS 中 margin 一样,用于控制根元素每一边的扩缩(单位为 px 或%),从而控制计算根元素和目标元素的交集的区域范围,默认值为 0。threshold
--- 阈值,回调函数触发的条件。取值范围为 0.0-1.0,默认值为 0.0。 IntersectionObserver
对像。
observe()
方法开始对目标元素进行监听。使用 IntersectionObserver()构造器创建 IntersectionObserver 对象并进行监听的语法如代码下所示:
let options = {
root: document.querySelector("#root"), //根元素
rootMargin: "0px", //传值形式类似于css的margin 传一个值则四个边都为0
threshold: 0, //触发条件 表示目标元素刚进入根元素时触发
};
//IntersectionObserver对象
let observer = new IntersectionObserver(callback, options);
let target = document.querySelector("#target"); //目标元素
observer.observe(target); //开始监听该目标元素
IntersectionObserver 接口(从属于Intersection Observer API
)提供了一种异步观察目标元素与根元素 交叉状态的方法。
当 IntersectionObserver 对象被创建时,就会被指定所监听的根元素、阀值等信息。一旦 IntersectionObserver 被创建后就无法更改其指定信息。
该对象的属性与方法如下图所示:
该对像的三个属性与IntersectionObserver()
构造器的 options 参数类似,并且这三个属性都只能读取操作,不能进行更改。
属性 | 说明 | 默认值 |
---|---|---|
root | 指定根元素。如果传值为 null,则为顶级文档的视窗。 | 顶级文档的视口(一般为 html) |
rootMargin | 根元素的扩缩边距。其传值形式与 CSS 中 margin 一样,用于控制根元素每一边的扩缩(单位为 px 或%),从而控制计算根元素和目标元素的交集的区域范围。单位为 px 或%。默 | "0px 0px 0px 0px" |
thresholds | 一个包含阈值的数组,并按升序排列,列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会触发回调函数。 | 0 |
该对象的四个方法如下表:
方法 | 说明 |
---|---|
observe(target) | 开始监听指定目标元素 |
unobserve(target) | 停止监听指定的目标元素 |
takeRecords() | 返回所有观察目标的 IntersectionObserverEntry 对象数组 |
disconnect() | 使 IntersectionObserver 对象停止全部监听工作 |
IntersectionObserverEntry
接口(从属于 Intersection Observer API)描述了目标元素与其根元素容器在某一特定过渡时刻的交叉状态。
IntersectionObserverEntry
对像数组作为entries
参数传递给IntersectionObserver
对像的回调函数中; 此外,这对象数组只能通过调用IntersectionObserver.takeRecords()
来获取。
该对象的主要属性如下图所示:
IntersectionObserverEntry 对象的七个属性都是只读属性,如下表所示:
属性 | 说明 |
---|---|
target | 返回目标元素,表示目前该对象正监听的元素 |
isIntersecting | 返回一个布尔值,目标元素刚出现在根元素可视区时返回 true;目标元素从根元素可视区消失返回 false;以上两种情况都会触发 callback 函数 |
boundingClientRect | 返回目标元素的矩形区域的信息,返回结果与element.getBoundingClientRect()相同 |
rootBounds | 返回根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null |
intersectionRect | 返回目标元素与视口(或根元素)的交叉区域的信息 |
intersectionRatio | 返回目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为 1,完全不可见时小于等于 0 |
time | 返回一个记录从IntersectionObserver的时间原点到交叉被触发的时间的时间戳 |
该 API 的兼容性如下:
详情大家可参考CAN I USE - intersectionobserver。
大家可以在自己电脑运行一下下面的代码,会有更深的理解。
<!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>
<style>
* {
margin: 0;
padding: 0;
}
.container > div {
margin: 5px auto;
width: 100px;
height: 100px;
outline: 1px solid red;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
<div class="item">11</div>
<div class="item">12</div>
<div class="item">13</div>
<div class="item">14</div>
<div class="item">15</div>
</div>
<script>
//io 为 IntersectionObserver对象 - 由IntersectionObserver()构造器创建
var io = new IntersectionObserver((entries) => {
//entries 为 IntersectionObserverEntry对像数组
entries.forEach((item) => {
//item 为 IntersectionObserverEntry对像
// isIntersecting是一个Boolean值,判断目标元素当前是否可见
if (item.isIntersecting) {
//div 可见时 进行相关操作
console.log(item.target.innerText);
io.unobserve(item.target); //停止监听该div DOM节点
}
});
}); //不传options参数,默认根元素为浏览器视口
const divArr = [...document.querySelectorAll(".item")];
divArr.forEach((div) => io.observe(div)); // 遍历监听所有div DOM节点
</script>
</body>
</html>
我已在 Vue2 使用Intersection Observer API
实现了图片懒加载效果哦,大家可以去看看。Vue2 中自定义图片懒加载指令 2.0
这是我目前所了解的知识面中最好的解答,当然也有可能存在一定的误区。
所以如果对本文存在疑惑,可以在评论区留言,我会及时回复的,欢迎大家指出文中的错误观点。
最后码字不易,觉得有帮助的朋友点赞、收藏、关注走一波。