项目精讲 v-lazy实现图片懒加载

我们首先需要创建一个自定义指令对象。每个自定义对象都有着钩子函数

js 复制代码
const directive = {
  created() {},   // 元素属性初始化前
  beforeMount() {},// 元素挂载到 DOM 前
  mounted() {},    // 元素挂载到 DOM 后 (当前代码使用的钩子)
  beforeUpdate() {},
  updated() {},    // 元素及子组件更新后
  beforeUnmount() {},
  unmounted() {}   // 元素从 DOM 移除后 (当前代码使用的钩子)
}

接着我们在main.js中注册一下这个自定义指令

js 复制代码
import directive form './active'

App
.directive('lazy',directive)
.mount(#app)

当我们的项目页面假如有大量的请求,这是一个非常耗费性能的事,我们可以通过自定义指令 v-lazy 来实现图片的懒加载

原理如下:当图片进入我们的视口内,将图片的src属性添加到预先存取的路径中。

这会用到一个InersectionObserver 对象,他的用法很简单。

ini 复制代码
var io = new IntersectionObserver(callback, option);

我们创建一个观察器实例对象。 他有一个observe方法,里面的参数就是观察的Dom节点。 每当Dom进入视口和离开时就会触发回调。option负责配置一些选择项。

callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

threshold属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。

ini 复制代码
new IntersectionObserver(
  entries => {/* ... */}, 
  {
    threshold: [0, 0.25, 0.5, 0.75, 1]
  }
);

用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。

很多时候,目标元素不仅会随着窗口滚动,还会在容器里面滚动(比如在iframe窗口里滚动)。容器内滚动也会影响目标元素的可见性,参见本文开始时的那张示意图。

IntersectionObserver API 支持容器内滚动。root属性指定目标元素所在的容器节点(即根元素)。注意,容器元素必须是目标元素的祖先节点。

dart 复制代码
var opts = { 
  root: document.querySelector('.container'),
  rootMargin: "500px 0px" 
};

var observer = new IntersectionObserver(
  callback,
  opts
);

上面代码中,除了root属性,还有rootMargin属性。后者定义根元素的margin,用来扩展或缩小rootBounds这个矩形的大小,从而影响intersectionRect交叉区域的大小。它使用CSS的定义方法,比如10px 20px 30px 40px,表示 top、right、bottom 和 left 四个方向的值。

这样设置以后,不管是窗口滚动或者容器内滚动,只要目标元素可见性变化,都会触发观察器。

注意:IntersectionObserver API 是异步的,是一个微任务,不随着目标元素的滚动同步触发。

这时候我们就能很简单的写出这个自定义指令v-lazy

js 复制代码
const options = {
    root: null,        // 观察基准:浏览器可视区域(viewport)
    rootMargin: '0px', // 可视区域不扩展
    threshold: 0       // 触发条件:元素刚进入视窗就触发(即使只露出1个像素)
};
// export function useIntersectionObserver;
const observer = createIntersectionObserver();

export function useIntersectionObserver() {
    return {
        observer,
    };
}
function createIntersectionObserver() {
    const observer = new IntersectionObserver((entries) => {

        entries.forEach((entry) => {
            if (entry.isIntersecting) {
                loadImage(entry.target as HTMLImageElement);
                observer.unobserve(entry.target); // 加载后取消观察
                // console.log("观察完毕");
            }
        });
    }, options);
    return observer;
}
function loadImage(targetImage: HTMLImageElement) {
    targetImage.src = targetImage.dataset?.src || "";
}

const lazyDirective = {
  mounted(el: HTMLImageElement, binding: DirectiveBinding) {
    if (typeof binding.value === 'string') {
      el.dataset.src = binding.value; // 将图片的URL存储在元素的dataset属性中
    }
    observer.observe(el);
  },
  unmounted(el: any) {
    // 当元素卸载时,取消观察
    observer.unobserve(el);  }
};

参考作者:阮一峰

相关推荐
weixin_382395232 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__2 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.3 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~4 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华4 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子7 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅7 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni7 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学8 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端