懒加载优化方案——IntersectionObserver

在加载图片列表时候,如果不做任何处理,浏览器会创建所有img标签并去加载每一张图片,当图片过大,或者过多的时候,页面将超级消耗资源,这时候,懒加载就起到了一个很重要的作用(如果有运维支持,当这篇文章不存在),只展示用户看到的(屏幕内的),所以官网提供了一个原生的APIIntersectionObserver接口;

从属于 Intersection Observer API)提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。

说白了,就是可判断监听元素与根是否有交叉

image.png

先创建一个页面,将这几个区域绘制出来,为了方便演示,结构可能比较反人类,凑合看~

div>ul>li*100>div>p{$}+img[src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b938334aec0d4f1886d0a16e45bcadc6~tplv-k3u1fbpfcp-watermark.image?"] 在vscode中输入这行指令,便可以创建出100个li并在里面加了序号和图片,

2023-08-07 11.59.13.gif

蓝色部分将作为根,粉色框为常显示,彩色背景为懒加载内容,这样比较方便观察,超出根的元素,彩色背景图消失,粉色框用来对比的,

基本用法

首先监听div的滚动,改变ul的top实现手动滚动方式 ```typescript const div = document.querySelector('div') const ul = document.querySelector('ul') div?.addEventListener('wheel', (e) => { console.log(); if (ul) { const n = ul.offsetTop if (e.wheelDelta > 0) { const top = n + 6; ul.style.top = top + 'px' } else {

const top = n - 6;
        ul.style.top = top + 'px'
    }

}

})

```

感谢喜欢你没道理 同学指正,以下为错误示范,问题原因为循环遍历的时候注册了多个IntersectionObserver实例。

typescript const lis = document.querySelectorAll('li') for (let i = 0; i < lis.length; i++) { const li = lis[i] const liDiv = li.querySelector('div') let intersectionObserver = new IntersectionObserver((entries) => { // 巴拉巴拉 }, { root: div, // root 根 }); // 开始监听 intersectionObserver.observe(li); }

应该将全局注册一个检测接口,目标元素为多个时,回调函数的参数为数组,遍历这个数组即可得到每一个交叉元素的信息,当循环li时,调用observe方法去监听当前li,这样全局只保持一个IntersectionObserver实例即可

```

// Intersection回调方法 const intersectionCallback = (entries) => { entries.forEach((entry) => { // console.log('entries',element) const li = entry.target let liDiv = li.querySelector('div') // 根据api返回的isIntersecting字段判断某元素是否展示 // 如果在react或者vue中 可以将没有交叉的元素置为null,这里不做展示 const flag = li.getAttribute('flag') if (flag !== 'true') { const cross = entry.intersectionRatio > 0 || entry.isIntersecting if (cross) { li.setAttribute('flag', 'true') } liDiv.style.display = cross ? "block" : 'none' } }); }

// 全局注册 Intersection let intersectionObserver = new IntersectionObserver(intersectionCallback, { root: div, // root 根 threshold: 1, rootMargin: '40px 40px 40px 40px' });

const lis = document.querySelectorAll('li') for (let i = 0; i < lis.length; i++) { const li = lis[i] // 开始监听 intersectionObserver.observe(li); }

```

2023-08-07 14.06.47.gif

问题

官网上提供的判断方式是if (entries[0].intersectionRatio <= 0) return;,根据intersectionRatio值是否大于零表示监听元素与根是否交叉,但是会有一个问题,滚轮滑动速度匀速还可以,但是一旦滚动速度稍微慢点,返回的值还是0,这种情况属于偶现,万能的测试提的bug。。。使用时请注意,在当前demo并没复现,原谅不能抛项目的画面,大家使用时注意就好。

优化

IntersectionObserver构造函数接受两个参数,第一个为监听回调,第二个参数options(可选),包含三个参数, 1. root根,也就是判断交叉的视口; 2. rootMargin盒模型边界偏移量,也相当于扩展的范围,参数跟css的属性参数写法一样 '40px 40px 40px 40px',类似这种,可以实现预加载的功能; 3. threshold,一个归一化的比例值,比如0 为'0%',0.3,为'30%',这个数值代表交叉的范围比例,如果是'50%',则根与被监听的元素交叉比例达到一半才去判定是否真正交叉

这样,我们就可以根据这些值,和其他方案结合,对页面进行优化,从上图能看出已经加载出来的元素也被反复的隐藏和显示(渲染或销毁),渲染过程反而更消耗性能,我们需要判断元素是否已经渲染过,如果已经渲染过,则下次超出边界不销毁,需要给被监听的元素添加一个属性flag用来标记,回调函数方法改造如下:

typescript const flag = li.getAttribute('flag') if (flag !== 'true') { const cross = e.intersectionRatio > 0 || e.isIntersecting if(cross) { li.setAttribute('flag', 'true') } liDiv.style.display = cross ? "block" : 'none' } 或许你可以自己选择一个喜欢的属性,再结合滚动加载和indexDB,就算后台返回一万条数据,也丝毫没有渲染压力,一般后台返回大数据量,都是城市信息,轨迹信息等,我还真没见过一次性请求那么多图片的接口

以上内容在官网 都有解释,只不过太多专业的名词,我大概根据自己的理解转成大白话写出来,还有很多没写在这里的API,请大家自主学习一下~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙华鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值