Vue3实现图片懒加载及自定义懒加载指令

这篇具有很好参考价值的文章主要介绍了Vue3实现图片懒加载及自定义懒加载指令。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

大家好,我是南木元元,热衷分享有趣实用的文章。图片懒加载是一种常见性能优化的方式,它只去加载可视区域图片,而不是在网页加载完毕后就立即加载所有图片,能减少很多不必要的请求,极大的提升用户体验。

图片懒加载的实现原理:在图片没进入可视区域的时候,只需要让 img 标签的 src 属性指向一张默认图片,在它进入可视区后,再替换它的 src 指向真实图片地址即可。

本文就分享一下在vue3中实现图片懒加载的几种方式,包括使用插件以及自定义指令,实现的最终效果如下图所示:
Vue3实现图片懒加载及自定义懒加载指令,vue,vue.js

使用vue3-lazyload插件

第一种方式就是使用插件,使用插件的方式非常简单,只需要简单的几步即可实现。

Vue2中可以使用vue-lazyload插件来实现图片懒加载,在Vue3中可以使用vue3-lazyload插件实现图片懒加载。

  • 1.安装vue3-lazyload插件
$ npm i vue3-lazyload
# or
$ yarn add vue3-lazyload
# or
$ pnpm i vue3-lazyload
  • 2.main.js入口文件注册插件
import { createApp } from "vue";
import App from "./App.vue";
//引入图片懒加载插件
import Lazyload from "vue3-lazyload";

const app = createApp(App);

//注册插件
app.use(Lazyload, {
   loading: "@/assets/images/default.png",//可以指定加载中的图像
   error: "@/assets/images/err.png",//可以指定加载失败的图像
});

app.mount("#app");
  • 3.模板中使用v-lazy指令来延迟加载图像
<template>
  <ul>
    <li v-for="item in imgList" :key="item.id">
      <img v-lazy="item.url" style="width: 100vw; height: 200px" />
    </li>
  </ul>
</template>

<script lang="ts" setup>
import { reactive } from "vue";
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => {
  return {
    id: `${i}`,
    url: `@/assets/images/${i}.jpg`,
  };
});
const imgList = reactive(data);
</script>

自定义v-lazy懒加载指令

  • 下面一种方式是自定义一个懒加载的指令,如何实现呢?

图片懒加载的核心是监听图片是否进入可视区域,如果进入就替换src,即懒加载指令的核心。

网上看了很多教程,大多都使用Element.getBoundingClientRect()等方法来获取相关元素的位置信息,然后监听scroll滚动条事件,这种方式逻辑略显复杂,而且频繁触发还会导致性能问题,需要做节流控制。

  • 有没有什么简化的方式呢?

可以通过VueUse中的useIntersectionObserver原生的IntersectionObserver api来简化判断图片是否进入可视区域,下面就分别通过这两种简化的方式来实现一个自定义的懒加载指令。

使用useIntersectionObserver

useIntersectionObserver是VueUse提供的一个方法,那么VueUse 是什么?

一款基于Vue组合式API的函数工具集。

以上是官方网站关于它的定义。

简单的说就是一个工具函数包,它可以帮助你快速实现一些常见的功能。比如下面的一些:

  • useLocalStorage:提供在本地存储中保存和获取数据的功能。
  • useMouse:提供跟踪鼠标位置和鼠标按下状态的功能。
  • useDebounce:提供防抖功能。
  • useThrottle:提供节流功能。
  • useIntersectionObserver:提供对元素是否可见进行观察的功能,可用于实现懒加载等效果。

接下来就通过使用useIntersectionObserver这个函数,来自定义一个懒加载指令。

  • 首先安装 VueUse
npm i @vueuse/core
  • main.js入口文件导入
import { createApp } from "vue";
import App from "./App.vue";

//从@vueuse/core中导入useIntersectionObserver函数
import { useIntersectionObserver } from "@vueuse/core";

const app = createApp(App);

app.mount("#app");
  • directive注册v-lazy全局指令
//main.js
//注册v-lazy全局指令,使v-lazy在所有组件中都可用
app.directive("lazy", {
  //节点挂载完成后调用
  mounted(el, binding) {
    useIntersectionObserver(el, ([{ isIntersecting }]) => {
      //判断当前监听元素是否进入视口区域
      if (isIntersecting) {
        el.src = binding.value;
      }
    });
  },
});

一个指令定义对象可以提供多个钩子函数,比如 mounted、updated、unmounted 等,我们使用mounted,也就是在节点挂载完成后调用。指令的钩子有两个主要的参数:el和binding。el是指令绑定到的元素,binding中使用最多的是value,即传递给指令的值,例如在 v-lazy=“imgSrc” 中,值是 imgSrc对应的真实图片地址。

然后使用useIntersectionObserver函数,它的两个参数,一个是需要监听的元素,另一个是回调函数,参数值isIntersecting为一个布尔值,用来判断当前监听元素是否进入视口区域,如果进入视口区域,那么我们就可以将图片的真实url赋值给图片的src。

其实上述代码还有不完善的地方,首先是重复监听的问题,可以进行console调试一下:

useIntersectionObserver(el, ([{ isIntersecting }]) => {
  console.log(isIntersecting);//测试
  if (isIntersecting) {
    el.src = binding.value;
  }
});

此时的效果如下图所示:
Vue3实现图片懒加载及自定义懒加载指令,vue,vue.js
从上图可以看到,往上滚动,监听过的图片会重复监听,这是我们不想要的,会造成性能浪费。

解决思路:在监听的图片第一次完成加载后就停止监听。可以利用useIntersectionObserver函数提供的stop方法,修改后的代码如下:

app.directive("lazy", {
  mounted(el, binding) {
    const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
      console.log(isIntersecting);
      if (isIntersecting) {
        el.src = binding.value;
        //在监听的图片第一次完成加载后就停止监听
        stop();
      }
    });
  },
});

完善后的效果如下,解决了重复监听问题。
Vue3实现图片懒加载及自定义懒加载指令,vue,vue.js
我们还可以设置一个默认图片,当图片还没加载完成时,就显示默认图片。

app.directive("lazy", {
  mounted(el, binding) {
    el.src = "@/assets/images/default.png"; // 使用默认图片
    const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
      if (isIntersecting) {
        el.src = binding.value;
        //在监听的图片第一次完成加载后就停止监听
        stop();
      }
    });
  },
});

此时还存在着的一个问题是,当前注册了一个全局的自定义指令,所有的代码逻辑全写在入口文件中,这样会造成代码的臃肿。

解决思路:拆分代码,通过插件的方法把懒加载指令封装为插件,main.js入口文件只需负责注册插件即可。

src下新建directive/index.js文件,专门存放自定义的插件,把代码逻辑进行转移。

// src/directive/index.js
import { useIntersectionObserver } from "@vueuse/core";
// 封装插件
export const lazyPlugin = {
  install(app) {
    app.directive("lazy", {
      mounted(el, binding) {
        el.src = "@/assets/images/default.png"; 
        const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
          if (isIntersecting) {
            el.src = binding.value;
            stop();
          }
        });
      },
    });
  },
};

然后在main.js中注册插件

import { createApp } from "vue";
import App from "./App.vue";
import { lazyPlugin } from "./directive";

const app = createApp(App);
//注册插件
app.use(lazyPlugin);
app.mount("#app");

定义插件可以参考Vue官网。通常一个 Vue3 的插件会暴露 install 函数,当 app 实例 use 该插件时,就会执行该函数。然后在 install 函数内部,通过 app.directive 去注册一个全局指令,这样就可以在组件中使用它们了。

现在的效果就和一开始介绍的效果一致了。
Vue3实现图片懒加载及自定义懒加载指令,vue,vue.js

使用IntersectionObserver

MDN上对IntersectionObserver的定义

  • IntersectionObserver:提供了一种异步观察目标元素与其祖先元素或顶级文档视口交叉状态的方法。当它被创建时,其被配置为监听根中一段给定比例的可见区域。当其监听到目标元素的可见部分(的比例)超过了一个或多个阈值(threshold)时,会执行指定的回调函数。

简单来说就是IntersectionObserver可以自动观察目标元素的可见性变化,并且是异步进行检测的,从而不需要使用复杂的逻辑或导致性能问题。

其实查看vue3-lazy源码和useIntersectionObserver源码,会发现,它们使用的就是IntersectionObserver api

那么我们也可以使用这个api来实现懒加载指令。代码如下:

// src/directive/index.js
import defaultImg from "@/assets/images/default.png";

export const lazyPlugin = {
  install(app) {
    app.directive("lazy", {
      mounted(el, binding) {
        el.src = defaultImg;
        //使用IntersectionObserver
        const io = new IntersectionObserver((entries) => {
          entries.forEach((item) => {
          	// isIntersecting属性判断目标元素当前是否可见
            if (item.isIntersecting) {
              el.src = binding.value;
              io.unobserve(item.target);//停止监听
            }
          });
        });
        io.observe(el);//监听目标元素
      },
    });
  },
};

IntersectionObserver对应的回调函数的参数 entries,是 IntersectionObserverEntry 对象数组。当观测的元素可见比例超过指定阈值时,就会执行该回调函数(默认阈值为 0,表示目标元素刚进入根元素可见范围时触发回调函数),对 entries 进行遍历,拿到每一个 entry,然后判断 entry.isIntersecting 是否为 true,如果是则说明 entry 对象对应的 DOM 元素进入了可视区。

上述代码已经实现了懒加载的基本效果,其实还可以进行一些优化,那就是封装一个方法,来实现图片的异步加载。

主要思想就是图片的预加载技术,用Image对象实例代替 img目标元素加载图片,让它去请求要加载的图片路径,成功后再替换 img 标签的 src,这样就完成了图片真实地址的加载。

import defaultImg from "@/assets/images/default.png";
//异步加载图片
let imageAsync = (url) => {
  return new Promise((resolve, reject) => {
    let img = new Image();
    img.src = url;
    img.onload = () => {
      resolve();
    };
    img.onerror = (err) => {
      reject(err);
    };
  });
};

export const lazyPlugin = {
  install(app) {
    app.directive("lazy", {
      mounted(el, binding) {
        el.src = defaultImg;
        const io = new IntersectionObserver((entries) => {
          entries.forEach((item) => {
            if (item.isIntersecting) {
              //使用异步加载图片
              imageAsync(binding.value)
              .then(() => {
                el.src = binding.value;//成功后再替换 img 标签的 src
              })
              .catch((error) => {
                console.log(error);
              });
              io.unobserve(item.target);
            }
          });
        });
        io.observe(el);
      },
    });
  },
};

参考资料:
https://www.npmjs.com/package/vue3-lazyload
https://cn.vuejs.org/guide/reusability/custom-directives.html
https://cn.vuejs.org/guide/reusability/plugins.html
https://www.vueusejs.com/core/useIntersectionObserver/
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

结语

本文主要分享了在vue3中实现图片懒加载的几种方式:使用vue插件、通过VueUse中的useIntersectionObserver以及IntersectionObserver原生api来自定义指令。
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~文章来源地址https://www.toymoban.com/news/detail-643641.html

到了这里,关于Vue3实现图片懒加载及自定义懒加载指令的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3自定义指令实现超出显示省略号,鼠标浮入弹出title,显示全部文本

    mounted 和 updated 是 Vue 生命周期钩子函数,分别表示 指令挂载到元素 和 指令所在组件更新时触发 。 el : HTMLElement 和 binding : DirectiveBinding 是方法参数, el 表示指令作用的元素, binding 包含指令的绑定值、参数和修饰符等信息。 el.offsetWidth 获取的是元素在渲染时所占据的整体

    2024年02月01日
    浏览(48)
  • Vue3: 自定义指令

    vue 官方提供了 v-for、v-model、v-if 等常用的内置指令。除此之外vue 还允许开发者自定义指令。 vue 中的自定义指令分为两类,分别是: ⚫ 私有自定义指令 ⚫ 全局自定义指令 在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。示例代码如下: 在使用自定义指令时

    2024年02月15日
    浏览(34)
  • vue3自定义指令

    在 Vue 3 中,我们可以通过使用 app.directive 方法来定义自定义指令。下面是一个简单的例子: 在上面的例子中,我们定义了一个名为 highlight 的自定义指令,它在元素被挂载时将其背景颜色设置为黄色,并在元素被卸载时将背景颜色重置为空。 在 mounted 和 unmounted 方法中,我们

    2024年01月18日
    浏览(41)
  • vue3自定义指令之防抖

    我们使用 vue 时,有时候需要用到自定义指令,例如一个防抖指令 现在有一个需求,用户在点击某个按钮时,我不希望用户在疯狂点击后,每次点击都会触发事件,像服务器发送请求,这并不是我们预期的,所以我们需要在用户点击是做防抖处理。那么怎么做到方便复用的解

    2024年02月16日
    浏览(38)
  • 【学习记录24】vue3自定义指令

    1、html部分  2、js部分 3、实现效果 1、html部分 2、js部分 在components下创建loading文件夹,在loading文件夹里创建directive.js  在main.js中全局注册指令 1、在components下创建loading文件夹,在loading文件夹里创建directive.js 2、在loading文件夹里创建loading.vue 3、在loading文件夹里放入一张G

    2024年01月19日
    浏览(43)
  • vue3:加载本地图片等静态资源

    在我们用 vue2 + webpack 的时候,加载图片资源是这样用的: 这样打包后就会触发 file-loader 打包图片资源,在 dist 文件夹中就可以看到这个图片(如果图片较小会打包进代码中变为 base64 引入)。 但是在 vue3 + vite 中,使用这种方式是不行的,vite 中没有 require 会报错。 解决方案

    2024年01月18日
    浏览(45)
  • vue3图片懒加载借助插件vue3-lazy react中的图片懒加载 借助插件 react-lazyload

    vue2 就用 vue-lazyload 这个喽 安装 npm install vue3-lazy 在main.ts中配置 在页面中使用 1, 下载安装懒加载模块 2, 在src/assets/目录下放入懒加载占位图 placeholder.gif 3, 在需要使用懒加载的组件中导入懒加载模块和占位图 4, 在组件rander函数中创建占位图片标签img 5, 在组件模板中给需要懒

    2024年02月11日
    浏览(47)
  • vue自定义指令v-loading(vue2和vue3)

      1. 目录结构: 2. 代码实现  /directives/loading/loading.vue    loading效果页面(此处使用的antd下面的组件,可自定义) /directives/loading/loading.js (实现loading组件的插入及销毁) /directives/loading/index.js (loading指令的注册) 3. 全局引入(main.js文件) 4. 使用 1. 目录结构 2. 代码实现

    2023年04月23日
    浏览(90)
  • Vue3的vite中图片的动态加载

    vite官网的静态资源引入参考地址 new URL() + import.meta.url 注意:这里只能通过 …/…/ 这种方式去获取路径,无法通过@/assets

    2024年02月16日
    浏览(38)
  • Vue.js 2.0 自定义指令

    除了默认设置的核心指令( v-model 和 v-show ),Vue 也允许注册自定义指令。注意,在 Vue2.0 里面,代码复用的主要形式和抽象是组件——然而,有的情况下,你仍然需要对纯 DOM 元素进行底层操作,这时候就会用到自定义指令。下面这个例子将聚焦一个 input 元素,像这样: 当页面

    2023年04月19日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包