数据懒加载是项目优化的一种类型
主要是监听, 监听的 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) {ponent('XtxSkeleton', XtxSkeleton)ponent('XtxCarousel', XtxCarousel)ponent('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="">