Vue3如何实现数据懒加载和图片懒加载

3,850 阅读3分钟

什么是懒加载?

懒加载就是延迟加载,是一种网页性能优化的方式,它能极大的提升用户体验;

为什么要使用懒加载?

22.png

如上图所示,

数据懒加载:当用户未滑到不可见区域时,数据已经请求回来了,数据较多情况下是必然发送很多次无效的请求,我们想要效果应该是等所在模块进入到 可视区 再发请求获取数据,这就是数据懒加载;

图片懒加载:其实和数据懒加载时一样的原理,当我们访问图片较多网站时,比如说各种商城页面,页面图片数量较多,且较大,若需要一次性加载完所有图片,性能消耗时间会较长,因此图片懒加载就可以让不可视区域的图片不去加载,避免一次性加载过多引起请求阻塞,减少服务器负载,减轻性能消耗。

如何实现

这里使用的第三方包 @vueuse/core 中的 useIntersectionObserver 来实现监听组件进入可视区域行为;需要配合vue3.0的组合API的方式才能实现;

1、数据懒加载

在项目中很多地方会使用到数据懒加载,避免代码复写我们可进行组件抽离封装,实际上唯一可能会随着业务使用发生变化的是 ajax接口的调用;

1.1单独封装数据懒加载

//导包
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
/*
  1.stop 一个可执行的函数用来停止监听行为
  2.target 一个由ref api调用之后形成的RefImpl对象 也可以是一个dom对象
  3.isIntersecting 一个类型为布尔值的数据,是否进入可视区域,当被监听元素进入视口区域时为true,离开视口区域时为false;
  4.observerElement  被观察的目标对象
  特别注意: 对于目标target是否进入视口区域的监听会一直进行不会只监听一次
*/

// 封装函数,后续使用直接调用useObserver函数即可

export function useObserver (apiFn) {
  // 通过 ref 获得组件实例
  const target = ref(null)
  const { stop } = useIntersectionObserver(
  
    // target 是观察的目标dom容器,必须是dom容器,而且是vue3.0方式绑定的dom对象
    target,
    
    // isIntersecting 是否进入可视区域,true是进入 false是移出
    ([{ isIntersecting }], observerElement) => {
    
      if (isIntersecting) {
      
       // 停止监听防止重复调用接口
        stop()
        
        // 简写:apiFn && apiFn()
        // 唯一不同的地方,以形参形式传入,有则调用这个请求 apiFn()
        if (apiFn) {
          apiFn()
        }
      }
    })
  return target
}

1.2组件内调用

<script>

import { reqFindHot } from '@/api/home'
import { ref } from 'vue'
import { useObserver } from '@/compositions'
export default {
  name: 'App',

  setup () {
    const goods = ref([])
    
    
    //发送ajax请求函数
    async function findHot () {
      const res = await reqFindHot()
      goods.value = res.result
    }

    // 通过 ref 获得组件实例,直接调用
    const target = useObserver(findHot)
    
    return {
      goods,
      target
    }
  }
}
</script>

2、图片懒加载

思路分析:

2.1 vue3全局指令的定义方式 app.directive;

注意:这里要区分Vue2Vue3全局注册区别

// vue2语法
   Vue.directive(指令名, {
	inserted (el, binding) {
        
     }
})

// vue3语法
  app.directive(指令名, {
    // 指令所在的元素,dom结构被解析完成
    mounted (el, binding) {
        
    }
})

2.2 以插件的形式注册全局指令; 2.3 编写懒加载指令的具体逻辑;

代码落地

1)以插件的方式,全局注册指令插件;

export default {
  install (app) {
    // 定义全局指令
  }
}

2)main.js中注册插件;


import directivePlugin from '@/directives'

//链式结构
createApp(App).use(directivePlugin).use(store).use(router).mount('#app')

3)编写指令逻辑

核心思路:监听图片是否进入到可视区,正式进入可视区之后将图片url数据交给img标签的src属性;

//图片加载失败所显示的默认图片
import defaltImg from '@/assets/images/200.png'

// 引入监听是否进入视口
import { useIntersectionObserver } from '@vueuse/core'

export default {
  // 需要拿到main.js中由createApp方法产出的app实例对象
  install (app) {
    // app实例身上有我们想要的全局注册指令方法  调用即可
    app.directive('imgLazy', {
    
      mounted (el, binding) {
        // el:img dom对象
        // binding.value  图片url地址
        // 使用vueuse/core提供的监听api 对图片dom进行监听 正式进入视口才加载
        // img.src = url
      
        const { stop } = useIntersectionObserver(
          // 监听目标元素
          el,
          ([{ isIntersecting }], observerElement) => {
            if (isIntersecting) {
              // 当图片url无效加载失败的时候使用默认图片替代
              el.onerror = function () {
                el.src = defaltImg
              }
              el.src = binding.value
              stop()
            }
          })
      }
    })
  }
}

4)使用

// 此处img是动态渲染出来的数据

   <img v-imgLazy="item.picture" alt="" />