前言
之所以是用这个Api,主要是为了优化项目。这个不光是可以用来实现图片的懒加载,还特别契合用于项目中的埋点。
优化之前:在页面加载以后获取元素节点的位置+滚动方式来做的。缺点显而易见,代码量比较多,而且监听scroll滚动官方也不推荐,非常影响性能。
优化之后:统一封装Class类,全局统一存储并处理createIntersectionObserver的监听。封装以后可以实现,横向滚动以及竖向滚动到可视区内都可以做到精准埋点。
使用方法
options的三个属性:
- thresholds:是一个数组,比如你可以设置[0.2,0.5,1]那么在视口区域相交部分达到20%、50%、100%都会触发监听。
- initialRatio:默认是0,这个参数基本不用改动的。
- observeAll:这个属性主要是用来监听多个类名一样的数据
IntersectionObserver 4个的方法
- relativeTo:以你选中的节点作为容器区域,并根据你传入的节点类名进行监听,当传入的类名出现在容器内就会触发。
- relativeToViewport:以你传入的选择器节点只要出现在视口区域就会触发监听。
- observe:传入需要监听的类名,回调函数 callback 包含一个参数 result
- disconnect:取消监听,隐藏或者卸载可取消监听。
vue3-代码示例
vue3中因为没有this,使用getCurrentInstance()
代码封装:
class ScrollObserver {
#instance;
#listeners;
constructor() {
this.#instance = getCurrentInstance();
this.#listeners = {};
}
static createObserver(selector, thresholds, observeAll = false) {
// 存在已经有的类名,则会报错,需要先取消监听
if (this.#listeners[selector]) this.#listeners[selector].disconnect();
this.#listeners[selector] = uni.createIntersectionObserver(this.#instance, {
thresholds,
observeAll,
});
return this.#listeners[selector];
}
on(selector,callback,observeAll = false) {
// 这里的监听[]可根据业务情况自行调整,监听满足条件是会重复触发
this.constructor.createObserver(selector, [0, 0.2, 1], observeAll)
.relativeToViewport({ right: 10 })
.observe(selector, (res) => {
callback(res);
});
}
// 取消
off(selector){
if (!this.#listeners[selector]) return
this.#listeners[selector].disconnect();
Reflect.deleteProperty(this.#listeners, selector);
}
}
margins: 用来扩展(或收缩)参照节点布局区域的边界
这里margin我是用的,{ right: 10 }。按照正常理解应该是当元素滑动到可视区域bottom以下才对,不过这个bottom是距离下边界的距离,相当于还是在元素内部。实际开发也可以修改参数进行微调测试。
使用示例:
// 实例化对象
const emitter = new ScrollObserver();
emitter.on('.example', (res) => {
if (res.intersectionRatio === 1) {// 业务代码 }
if (res.intersectionRatio === 0) {// 业务代码}
});
存在的问题
- 微信小程序:可以使用scss >>> 穿透语法实现组件的类名监听
- 支付宝小程序:不可以使用 >>>,目前没有更好的办法兼容,官方推荐使用this.createIntersectionObserver,不过vue3并没有this。
- 对已存在监听的类名,方法会抛出错误,需要先取消监听,然后再重新创建。
- 页面的onhide或者onshow以后,虽然是出现在了当前可视区域,这种情况是没有触发监听的。
总结
在小程序开发中,使用这个Api来实现埋点的数据上报,基本是相当精准的,而且不会对性能造成影响。