本文主要涵盖三个内容:
- 被观察的element的预处理
- IntersectionObserver的初始化
- 如何挂载全局指令及指令的使用
懒加载的核心定义
只加载当前可视区域(Viewport)内的图片,当用户滚动页面时,再按需加载即将进入可视区域的图片。根据定义我们可以拆解出:
- 只针对可视区域
- 滚动停止时,才加载可视区域图片
- 滚动过程中略过的图片都不纳入加载范围
存储观察的element以及批量处理视图内的图片
const loadingSet = new Set(); // 使用 Set 存储待加载图片
const debounceLoadImages = _.debounce((loadingSet) => {
loadingSet.forEach((img) => {
if (img.dataset.loaded) {
// 已加载图片无需重复处理
loadingSet.delete(img);
observer.unobserve(img);
return;
}
img.src = img.dataset.src; // 加载真实图片路径
img.dataset.loaded = true; // 设置标识,表示已加载
observer.unobserve(img); // 停止观察
loadingSet.delete(img); // 从集合中移除
});
}, 500);
- 这里使用集合loadingSet变量存储需要加载的图片
- 定义debounceLoadImages方法替换图片的真实路径,并增加防抖,避免快速滚动时无需加载的element也加载
- 取消对已加载element的观察且移除出集合loadingSet
IntersectionObserver的初始化
const observer = new IntersectionObserver((entries) => {
entries.forEach(({ isIntersecting, target }) => {
// 如果已经加载过,直接跳过
if (target.dataset.loaded) {
return;
}
if (isIntersecting) {
loadingSet.add(target); // 进入视口,加入待加载集合
} else {
loadingSet.delete(target); // 离开视口,从集合中移除
}
});
// 批量加载
debounceLoadImages(loadingSet);
});
- 创建观察实例observer,后面会指定只有这一个观察实例
- 根据前文提到的dataset.loaded属性判断element是否应该被观察
- 调用debounceLoadImages批量加载的方法
指令的声明与使用
// 指令声明
export default {
mounted(el, binding) {
const { src } = binding.value;
el.dataset.src = src; // 存储真实图片地址
el.src = 'img/002.14463f8f.png'; // 设置占位图
observer.observe(el);
}
};
- 导出该lazy.js文件,在mounted生命周期中,替换图片的真实路径并存储真实的路径(这里的占位图的路径可以按照自己的需要去修改)
import lazyImg from "@/directives/lazy.js";
const app = createApp(App);
app.directive('lazy', lazyImg)
- 在main.js文件中声明该指令
<img v-for="img in imageArr"
:key="img"
class="img-item"
v-lazy="{src: require(`@/assets/img/00${img}.png`)}" />
在实际使用场景中使用v-lazy指令,这里因为是我个人的项目,所以使用require动态载入本地的图片,可以按自己的需要修改图片地址
实测效果图:
这里我快速滚动到底部就是为了模拟用户如果快速滚动图片列表到某一位置时,中间略过的图片应该是不加载的姿态,在network中不会去获取该图片的资源。