数据懒加载是项目优化的一种类型
主要是监听, 监听的 DOM 元素是否有进入可视区
进入了可视区就可以做一些事情, 如: 发送请求, 返回出数据
离开可视区停止对 DOM 元素的监听
首先我们使用的是和 Vue3 配套的一个工具提供的一个方法
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
/**
* 数据懒加载函数
* @param {Function} apiFn - api函数
*
**/
export const useLazyData = (apiFn) => {
// 初始化一个目标对象
const target = ref(null)
const result = ref([])
// 这个函数调用的返回值是一个对象, 此对象中有一个成员stop函数
// 它使用用来停止监听的函数
// 此函数的第一个参数是监听的DOM对象
// 第二个参数是一个回调函数
const { stop } = useIntersectionObserver(
target,
// isIntersecting它是一个布尔值, 如果监听对象进入了可视区; 值为true, 否则为false
// observerElement就是观察的元素
([{ isIntersecting }], observerElement) => {
if (isIntersecting) {
stop()
apiFn().then(data => {
result.value = data.result
})
}
},
// 第三个参数是交叉比例
{
threshold: 0
}
)
return { target, result }
}
const { target, result } = useLazyData(findHot)
上面做数据懒加载的效果是使用的别人封装好的方法来进行实现的
那么现实我们实现图片懒加载的效果, 我们就自己手写原生来实现吧
我们主要使用到的API是 IntersectionObserver
// 创建观察对象实例
const observer = new IntersectionObserver(callback[, options])
// callback 被观察dom进入可视区离开可视区都会触发
// - 两个回调参数 entries , observer
// - entries 被观察的元素信息对象的数组 [{元素信息},{}],信息中isIntersecting判断进入或离开
// - observer 就是观察实例
// options 配置参数
// - 三个配置属性 root rootMargin threshold
// - root 基于的滚动容器,默认是document
// - rootMargin 容器有没有外边距
// - threshold 交叉的比例
// 实例提供两个方法
// observe(dom) 观察哪个dom
// unobserve(dom) 停止观察那个dom
图片懒加载的原理就是, 使用自定义指令去控制监听的 img 标签的 src 的值
当 img 标签的 src 没有值的时候是不会发送请求的, 所以我们可以通过这一点来完成需求
思路分析:
1. 首先我们需要定义一个自定时指令(directive)
2. Vue3 中和 Vue2 中自定义指令的定义有所不同, Vue3 组件的实例是 app; Vue2 组件实例是vue
3. 且 Vue2 中使用的方法是 inserted, Vue3 中使用的是 mounted
4. 通过 mouted 方法获取到 el 和 binding 两个参数, 动态的修改 el.src 的值即可
import XtxSkeleton from './xtx-skeleton.vue'
import XtxCarousel from './xtx-carousel.vue'
import XtxMore from './xtx-more.vue'
import defaultImg from '@/assets/images/200.png'
export default {
install (app) {
app.component('XtxSkeleton', XtxSkeleton)
app.component('XtxCarousel', XtxCarousel)
app.component('XtxMore', XtxMore)
// 图片懒加载自定义指令
defineDirective(app)
}
}
const defineDirective = (app) => {
// 自定义指令第一个参数是, 自定义指令名称
// 第二个参数是, 自定义指令的配置项
app.directive('lazy', {
// el是添加自定义指令的DOM元素
// binding接收到自定义指令传入的值
mounted (el, binding) {
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
if (isIntersecting) {
observer.unobserve(el)
// 当DOM元素加载失败时, 走的回调函数
el.onerror = () => {
el.src = defaultImg
}
el.src = binding.value
}
}, {
threshold: 0
})
observer.observe(el)
}
})
}
/* 这里就不用添加src属性了, 因为自定义指令内部已经处理了 */
<img v-lazy="item.picture" alt="">