目录
1、懒加载介绍
2、实现懒加载技术的方案
3、具体实现代码
1、懒加载介绍
当页面需要展示大量图片时,如果一次性渲染所有图片,会向服务器发出大量请求,导致服务器响应慢,出现页面卡顿或崩溃等问题。采用懒加载技术只预先加载可视区内的图片,当滚动到其他位置时,才去加载这块区域的图片,也可以使用比较小的loading图片进行占位,有效减轻服务器的压力,加速页面渲染,提高用户体验。
2、实现懒加载技术的方案
(1)使用element-ui 的<el-image v-for="url in urls" :key="url" :src="url" lazy></el-iamge>可通过lazy开启懒加载功能,当图片滚动到可视范围内才会加载。
(2)使用vue-lazyLoad插件实现
(3)使用IntersectionObserver Api监听某些节点是否出现在可视区域内。
(4)offsetTop(图像距离顶部的高度)-scrollTop(页面被卷去的高度)<=window.innerHeight(可视区高度)才加载
(5)获得页面中某个元素的上边相对浏览器视窗的位置getBoundingClientRect().top<=window.innerHeight(可视区高度)才加载
3、具体实现代码
(1)element-ui <el-image> Element - The world's most popular Vue UI framework
(2)使用vue-lazyLoad插件实现,安装【npm i vue-lazyload -S】,main.ts中进行注册
//main.ts
import VueLazyload from 'vue-lazyload'
app.use(VueLazyload,
{preLoad: 1.3, //预加载的宽高比loading:
loadimage:"./assets/vue.svg", //图片加载状态下显示的图片
// error: "", //图片加载失败时显示的图片
attempt: 1, // 加载错误后最大尝试次数
})
页面中通过v-lazy使用
//xx.vue
<template>
<div>
<div v-for="item in arr" :key="item">
<img v-lazy="item" width="600" height="200" alt="" />
</div>
</div>
</template>
<script lang="ts" setup>
//glob是懒加载模式,globEager静态加载
let imageList: Record<string, { default: string }> = import.meta.glob(
"../assets/images/*.*",
{ eager: true }
);
let arr = Object.values(imageList).map((v) => v.default);
</script>
<style lang="scss" scoped></style>
- 【import.meta.glob("/xx")】方式 为懒加载模式,动态导入,构建时,会分离为独立的 chunk,该方式导入使用()=>import("/xx"),跟路由懒加载是一样的导入方式。
- 【import.meta.globEager("/xx")】以及【import.meta.glob("/xx",{eager:true})】为静态加载
打印imageList如图
(3)使用IntersectionObserver Api监听某些节点是否出现在可视区域内。
intersectionRatio表示节点出现在可视区的比例,isIntersecting为布尔值,为true表示出现在可视区
<template>
<div>
<div v-for="item in arr" :key="item">
<img v-lazy="item" width="600" height="200" alt="" />
</div>
</div>
</template>
<script lang="ts" setup>
import type { Directive } from "vue";
//glob是懒加载模式,globEager静态加载
let imageList: Record<string, { default: string }> = import.meta.glob(
"../assets/images/*.*",
{ eager: true }
);
console.log(">>>>>>>>>>>>>>>imageList", imageList);
let arr = Object.values(imageList).map((v) => v.default);
console.log(arr);
// 使用自定义指令
let vLazy: Directive<HTMLImageElement, string> = async (el, bingding) => {
let url = await import("../assets/vue.svg");
el.src = url.default;
let observer = new IntersectionObserver((entry) => {
console.log(entry[0], el);
if (entry[0].intersectionRatio > 0 && entry[0].isIntersecting) {
el.src = bingding.value;
observer.unobserve(el);//停止监听el对象
}
});
observer.observe(el); //开启监听el对象
};
</script>
<style lang="scss" scoped></style>
(4)判断图像的offsetTop(距离顶部的高度)-scrollTop(页面被卷去的高度)<=window.innerHeight(可视区高度)才加载
<template>
<div>
<div v-for="item in arr" :key="item">
<img
ref="img"
:data-src="item"
src="../assets/vue.svg"
width="600"
height="200"
alt=""
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from "vue";
//glob是懒加载模式,globEager静态加载
let imageList: Record<string, { default: string }> = import.meta.glob(
"../assets/images/*.*",
{ eager: true }
);
let arr = Object.values(imageList).map((v) => v.default);
const winHeight = window.innerHeight; // 浏览器可视区的高度
onMounted(() => {
var imgs = document.querySelectorAll("img");
let num = Math.ceil(winHeight / 200);
loadImg(imgs, 0, num); // 1.只加载可视区域内的图片-适用于图片高度固定的场景
window.addEventListener("scroll", function () {
loadImg(imgs, num, imgs.length); // 2.滚动时加载其他未加载的图片
});
});
function loadImg(img, startIdx, num) {
for (let i = startIdx; i < num; i++) {
if (startIdx == 0) {
img[i].src = img[i].dataset.src;
} else {
var imgOffsetTop = img[i].offsetTop; // 图片距顶部的高度
var scrollTop = document.documentElement.scrollTop; // 页面被卷去的高度
if (imgOffsetTop - scrollTop <= winHeight) {
// 判断图片是否将要出现
img[i].src = img[i].dataset.src; // 出现后将自定义地址转为真实地址
}
}
}
}
</script>
(5)获得页面中某个元素的上边相对浏览器视窗的位置getBoundingClientRect().top<=window.innerHeight(可视区高度)才加载
<template>
<div>
<div v-for="item in arr" :key="item">
<img
ref="img"
:data-src="item"
src="../assets/vue.svg"
width="600"
height="200"
alt=""
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
let timer = ref<number>(0);
//glob是懒加载模式,globEager静态加载
let imageList: Record<string, { default: string }> = import.meta.glob(
"../assets/images/*.*",
{ eager: true }
);
console.log(imageList);
let arr = Object.values(imageList).map((v) => v.default);
const winHeight = window.innerHeight; // 浏览器可视区的高度
onMounted(() => {
var imgs = document.querySelectorAll("img");
let num = Math.ceil(winHeight / 200);
loadImg(imgs, 0, num); // 1.只加载可视区域内的图片-适用于图片高度固定的场景
window.addEventListener("scroll", (e) => {
loadImg(imgs, num, imgs.length); // 2.滚动时加载其他未加载的图片
});
});
function loadImg(img, startIdx, num) {
for (let i = startIdx; i < num; i++) {
if (startIdx == 0) {
img[i].src = img[i].dataset.src;
} else {
const rect = img[i].getBoundingClientRect(); //getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
if (rect.top < window.innerHeight) {
img[i].src = img[i].dataset.src;
}
}
}
}
</script>
注意:获取元素节点要放在onMounted钩子中,因此此时Dom才挂载完成