懒加载、触底加载和预加载
我们知道我们在实际开发中,我们有些场景会遇见页面的触底加载,或者当要请求的资源很多的时候,为了提高页面性能,让页面更快的展示内容,很可能需要一些懒加载。我最开始学习前端的时候,就是学的比较笨的方法,使用scroll
事件监听,然后使用box.getBoundClientRect().top
与window.innerHeight
进行比较。(还有更麻烦的方法:box.offsetTop-body.scrollTop
的值与window.innerHeight或者body.clientHeight
).
...
<img id="#img" data-src='https://xxxxxxxx.jpg'></img>
...
<script>
window.addEventListener('scroll',function() {
if(img.getBoundingClientRect().top<window.innerHeight) {
let src = img.getAttribute('data-src');
img.setAttribute('src',src);
}
})
</script>
但是我们也会发现一个问题,那就是给window添加scroll
事件后,会频繁触发这个事件,会导致性能变差,会消耗很多资源。所以后面我又学到了新的方法,那就是使用 IntersectionObserver
这个新的API。
IntersectionObserver介绍
它是一个构造函数,可以创建一个对象,这个对象用于监视某个(某些)节点。在创建这个对象的时候,可以传递两个参数:第一个参数是一个回调函数,当监听的节点对象达到某个触发条件时就会触发,第二个参数是可选的,是一个配置参数,用于改变创建对象的一些属性(不传就使用默认)。
let observe = new IntersectionObserver(callback,options);
这个对象上有几个方法(在其IntersectionObserver构造函数的原型上),分别是:
- observe:对元素target添加监听,当target元素变化时,就会触发上述的回调
- unobserve:移除一个监听,移除之后,target元素的可视区域变化,将不再触发前面的回调函数
- disconnect:停止所有的监听
- takeRecords:返回一个对象数组,每个对象包含目标元素与根每次的相交信息。
备注: 如果使用回调来监视这些更改,则无需调用takeRecords方法。调用此方法会清除挂起的相交状态列表,因此不会运行回调。
我们在创建observe对象后,它有一个回调方法,这个回调方法会在创建的时候调用一次(当有监视的对象时),然后当满足一些条件后,会再触发:这个条件就是当监视的节点对象进入显示窗口时和离开显示窗口时。
但是我们怎样知道是哪一个节点对象进入窗口触发的呢?回调函数有个参数,这个参数返回的就是observe
对象所监视的所有对象。且当被监视的对象进入窗口后,这个对象的isIntersecting
就会变为true,我们还可以通过这个对象的target
属性拿到这个对象是监视的哪一个节点。
let observe = new IntersectionObserver((arr) => {
arr.forEach(obj=>{
let target = obj.target;
if(obj.isIntersecting) {
//进行操作
}
})
});
observe.observe(img);
observe.observe(body);
这样我们就可以基于此进行图片的懒加载或者页面的触底加载啦!
let observe = new IntersectionObserver((arr) => {
arr.forEach(obj=>{
let target = obj.target;
if(obj.isIntersecting) {
let src = img.getAttribute('data-src');
img.setAttribute('src', src);
observe.unobserve(target);
}
})
});
let imgs = document.querySelectorAll('img');
imgs.forEach(img=>{
observe.observe(img);
})
这个好处就是当图片加载完成后,就取消对图片的监视,然后当所有的都加载完成后,就不会再触发这个事件,从而节约资源。
而触底加载原理就是当页面的<footer>
进入可视区时,就发送ajax
请求,从而达到返回的数据再加载数据。
预加载
但是我们发现这种有时候效果不太好,因为要当内容已经进入可视化区域后才进行加载,这样就会有一个停顿进行内容的加载。所以我们可以通过预加载进行优化,就是当内容快要进入可视化区域时就加载!
传统的方式
使用传统的方式进行预加载,也就是让img.getBoundingClientRect().top-window.innerHeight > 某个值
的时候就进行加载。这个值可以是50,100。
IntersectionObserver
但是我们说过传统的方式消耗性能,那么用IntersectionObserver
如何进行预加载呢?我们可以在初始化observe实例时传递第二个参数:options配置项,将 rootMargin
的值修改一下(默认是0)。
rootMargin
rootMargin和css的margin差不多,就是给这个属性添加一个外边距,不过rootMargin是给root添加,root默认是浏览器窗口,当然我们可以在配置项里改变它。
我们给浏览器窗口添加外边距后,我们的浏览器窗口就变大了,这时observe的回调函数的触发就不再是监视的节点对象进入可视区域了,而是进入margin值时就会触发了。
所以我们这样就可以就行预加载:
let observe = new IntersectionObserver((arr) => {
arr.forEach(obj=>{
let target = obj.target;
if(obj.isIntersecting) {
//进行业务处理,什么发送ajax请求、获取图片等等...
}
})
},{
rootMargin: "0px 0px 100px 0px"
});
这样在监视的节点距离可视区域100px时,就会进行图片的加载了。
转载自:https://juejin.cn/post/7136768939337449479