什么是懒加载?
懒加载就是延迟加载,是一种网页性能优化的方式,它能极大的提升用户体验;
为什么要使用懒加载?
如上图所示,
数据懒加载:当用户未滑到不可见区域时,数据已经请求回来了,数据较多情况下是必然发送很多次无效的请求,我们想要效果应该是等所在模块进入到 可视区 再发请求获取数据,这就是数据懒加载;
图片懒加载:其实和数据懒加载时一样的原理,当我们访问图片较多网站时,比如说各种商城页面,页面图片数量较多,且较大,若需要一次性加载完所有图片,性能消耗时间会较长,因此图片懒加载就可以让不可视区域的图片不去加载,避免一次性加载过多引起请求阻塞,减少服务器负载,减轻性能消耗。
如何实现
这里使用的第三方包 @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;
注意:这里要区分Vue2和Vue3全局注册区别
// 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="" />