1.应用场景
如果访问的页面数据过多,为减少并发量,减少系统资源的消耗,这时候就需要用到懒加载,实现当组件进入可视区后再去发请求加载数据。
2.思路分析
如何判断一个dom元素是否进入了可见区域?
传统:获取dom位置 → getBoundingClientRect()
现在:直接判断元素进入可视区域的比例 → IntersectionObserver()
3.使用
① 定义一个target,并导出,监测谁,就在谁身上通过ref调用。
② 引入 useIntersectionObserver 并使用
- target 是观察的目标容器 dom对象 | 组件对象
- stop 是一个函数。如果调用它,就会停止观察(是否进入或移出可视区域的行为)
- isIntersecting 是一个bool值,表示是否进入可视区域。 true表示 进入 false表示 移出
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
export default {
setup () {
const target = ref(null)
const { stop } = useIntersectionObserver(target, ([{ isIntersecting }], observerDom) => {
console.log('home-hot', isIntersecting, stop)
// 如果target可见
if (isIntersecting) {
// 1.停止观察
stop()
// 2.发ajax
findHot().then(data => {
goods.value = data.result
})
}
})
return { target }
}
}
4.封装通用逻辑
4.1 原因
在项目中,如果多个组件都需要用到懒加载,这时候每次都在组件中引入使用,会产生大量重复代码。
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
/**
* 功能: 数据懒加载
*
* @param {*} fn 当目标可见时,要调用一次的函数
* @returns target: 要观察的目标(vue3的引用)
*/
const useLazyData = (fn) => {
const target = ref(null)
const { stop } = useIntersectionObserver(
target, // target 是vue的对象引用。是观察的目标
// isIntersecting 是否进入可视区域,true是进入 false是移出
// observerElement 被观察的dom
([{ isIntersecting }], observerElement) => {
if (isIntersecting) { // 目标可见,
// 1. ajax可以发了,后续不需要观察了
stop()
// 2. 执行函数
fn()
}
}
)
return target
}
export default useLazyData
4.2 在组件中使用
import { findNew } from '@/api/home'
import useLazyData from '@/compositions'
export default {
name: 'HomeNew',
setup () {
const fn = () => {
findNew().then(data => {
console.log('findNew', data)
list.value = data.result
})
}
const target = useLazyLoad(fn)
return { target }
}
}
5滚动加载的bug
如果需要懒加载的组件面积比较大,页面需要滚动比较多的面积才回去加载数据,用户体验不好。
5.1 原因
useIntersectionObserver 默认以进入视口的面积占总元素面积的0.1为基准判断是否进入,而这里的默认比例0.1在元素比较大的时候,会有这个问题:看起来已经可见了,但是我们回调没有执行。
5.2 解决
补充一个参数:threshold ,容器和可视区交叉的占比(进入的面积/容器完整面积) 取值,0-1 之间,默认是0.1,所以需要滚动较多才能触发进入可视区域事件。
const { stop } = useIntersectionObserver(
container,
([{ isIntersecting }], dom) => {
if (isIntersecting) {
stop()
fn()
}
}, {
threshold: 0
}
)