Maison > interface Web > Voir.js > Vous guide étape par étape pour personnaliser une instruction de chargement différé d'image dans Vue2

Vous guide étape par étape pour personnaliser une instruction de chargement différé d'image dans Vue2

青灯夜游
Libérer: 2022-09-22 20:29:48
avant
2172 Les gens l'ont consulté

Comment personnaliser les instructions de chargement différé de l'image dans

Vue ? L'article suivant vous donnera une introduction détaillée à l'instruction de chargement différé d'image personnalisée "v-lazy" dans Vue2. J'espère que cela vous sera utile !

Vous guide étape par étape pour personnaliser une instruction de chargement différé d'image dans Vue2

Lorsque je développais la page frontale de mon blog personnel, je voulais optimiser la vitesse de réponse du site Web, je voulais donc obtenir l'effet de chargement paresseux des images.

Je l'ai implémenté via la commande personnalisée v-lazy, j'aimerais donc partager avec vous le processus de développement de cette commande et les solutions à ses difficultés. [Recommandations associées : tutoriel vidéo vuejs自定义指令v-lazy实现的,所以在这跟大家分享一下这个指令的开发流程及其难点的解决方法。【相关推荐:vuejs视频教程

1.涉及到的主要知识讲解

自定义图片懒加载指令主要涉及以下三块知识:

  • Vue2 中自定义指令
  • 使用事件总线进行模块之间的通信
  • 使用到的 Web API
    • Element.clientHeight
    • Element.getBoundingClientRect()

下面我会对这些知识点进行一一介绍。

1.1 Vue2 中自定义指令

下面我只对自定义指令做简单的介绍,详细介绍大家可以参照Vue 官网 - 自定义指令

1.1.1 指令对象的钩子函数

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。可通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数的参数主要有这四个el、binding、vnode、oldVnode

1.1.2 钩子函数参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,如:v-my-directive="1 + 1" 中,绑定值为 2。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

1.2 使用事件总线进行模块之间的通信

对事件总线不熟悉的朋友,可以参照该博客什么是 Vue 事件总线(EventBus)

  • 监听事件总线上的事件---调用 $on 方法
  • 触发事件总线上的事件---调用 $emit 方法
  • 取消监听事件总线上的事件---调用 $off 方法

我们可以借助 vue 示例来实现事件总线,也可以自行封装;我使用了第一种方法。

因此事件总线配置文件---eventBus.js 的代码如下:

import Vue from "vue";
const eventBus = new Vue({});
/*
 * 事件名:mainScroll
 * 含义:主区域滚动条位置变化后触发
 * 参数:
 * - 滚动的dom元素,如果是undefined,则表示dom元素已经不存在
 */
//在Vue.prototype原型上注册事件总线,方便vue实例对象监听和触发
Vue.prototype.$bus = eventBus;
//导出事件总线,方便在其他js模块监听和触发事件总线上的事件
export default eventBus;
Copier après la connexion

1.3 使用到的 Web API

1.3.1 Element.clientHeight

首先Element.clientHeight]

1. Explication des principales connaissances impliquées

🎜L'instruction de chargement différé d'image personnalisée implique principalement les trois connaissances suivantes : 🎜
  • Instructions personnalisées dans Vue2
  • Utiliser le bus d'événements pour la communication entre les modules
  • API Web utilisée
    • Element.clientHeight
    • Element.getBoundingClientRect()
    • ul>
🎜Je présenterai ces points de connaissance un par un ci-dessous. 🎜

🎜1.1 Instructions personnalisées dans Vue2🎜

🎜Je ne présenterai que brièvement les instructions personnalisées ci-dessous Pour une introduction détaillée, vous pouvez vous référer à Directive personnalisée du site Web officiel Vue🎜. 🎜

🎜1.1.1 Fonction Hook de l'objet instruction🎜

  • bind : appelée une seule fois, appelée lorsque l'instruction est liée à l'élément pour la première fois. Les paramètres d'initialisation uniques peuvent être effectués ici.
  • Inséré : appelé lorsque l'élément lié est inséré dans le nœud parent (seul le nœud parent est garanti d'exister, mais pas nécessairement inséré dans le document).
  • update : appelé lorsque le VNode du composant est mis à jour, mais peut se produire avant que son VNode enfant ne soit mis à jour. La valeur de la directive peut avoir changé ou non. Les mises à jour inutiles du modèle peuvent être ignorées en comparant les valeurs avant et après la mise à jour (voir ci-dessous pour les paramètres détaillés de la fonction hook).
  • componentUpdated : Appelé après que tous les VNode du composant où se trouve l'instruction et ses sous-VNodes ont été mis à jour.
  • unbind : appelé une seule fois, lorsque l'instruction n'est pas liée à l'élément.
🎜Les paramètres de la fonction hook incluent principalement ces quatre el, contraignant, vnode, oldVnode. 🎜

🎜1.1.2 Paramètres de la fonction Hook🎜

  • el : L'élément lié à l'instruction peut être utilisé pour faire fonctionner directement le DOM.
  • binding : un objet contenant les propriétés suivantes :
    • name : le nom de la directive, à l'exclusion du préfixe v-.
    • value : La valeur de liaison de la directive, telle que : v-my-directive="1 + 1", la valeur de liaison est 2.
    • oldValue : valeur précédente de la liaison d'instruction, disponible uniquement dans les hooks update et componentUpdated. Disponible même si la valeur a changé.
    • expression : expression d'instruction sous forme de chaîne. Par exemple, dans v-my-directive="1 + 1", l'expression est "1 + 1".
    • arg : paramètres passés à la commande, facultatifs. Par exemple, dans v-my-directive:foo, le paramètre est « foo ».
    • modificateurs : un objet contenant des modificateurs. Par exemple : dans v-my-directive.foo.bar, l'objet modificateur est { foo: true, bar: true }.
  • vnode : Le nœud virtuel généré par la compilation Vue. Passez à l’API VNode pour en savoir plus.
  • oldVnode : le nœud virtuel précédent, disponible uniquement dans les hooks update et componentUpdated.

🎜1.2 Utiliser le bus d'événements pour la communication entre les modules🎜

🎜Les amis qui ne sont pas familiers avec le bus d'événements peuvent se référer à ce blogQu'est-ce que Vue Event Bus (EventBus)🎜. 🎜
  • Écouter les événements sur le bus d'événements --- appeler la méthode $on
  • Déclencher des événements sur le bus d'événements --- appeler la méthode $emit
  • Annuler l'écoute des événements Événements sur le bus --- appelez la méthode $off
🎜Nous pouvons utiliser l'exemple vue pour implémenter le bus d'événements, ou nous pouvons l'encapsuler nous-mêmes ; 🎜🎜Donc le code du fichier de configuration du bus d'événements ---eventBus.js est le suivant : 🎜
import Vue from "vue";
import App from "./App.vue";
import "./eventBus"; //引入事件总线
import vLazy from "./directives/lazy";
Vue.directive("lazy", vLazy); //全局注册指令
new Vue({
  render: (h) => h(App),
}).$mount("#app");
Copier après la connexion
Copier après la connexion

🎜1.3 API Web utilisée🎜 h3>

🎜1.3.1 Element.clientHeight🎜

🎜Tout d'abord, Element.clientHeight est une 🎜propriété en lecture seule🎜 avec les caractéristiques suivantes : 🎜
  • 对于那些没有定义 CSS 或者内联布局盒子的元素,该 API 会返回 0;
  • 对于根元素(html 元素)或怪异模式下的 body 元素,该 API 将返回视口高度(不包含任何滚动条)
  • 其他情况,该 API 会返回元素内部的高度(以像素为单位),包含contentpadding,不包含bordermargin与水平滚动条(如果存在)。

另外改 API 会将获取的值四舍五入取整数。如果你需要小数结果,可以使用 element.getBoundingClientRect()方法。

示例图如下:

Vous guide étape par étape pour personnaliser une instruction de chargement différé dimage dans Vue2

该 API 的详细文档可参照MDN - Element.clientHeight

1.3.2 Element.getBoundingClientRect()

Element.getBoundingClientRect()方法返回一个DOMRect对象,其提供了元素的大小及其相对于视口的位置。 该方法无参数,返回值为DOMRect对象,该对象的属性以下几个:

  • width:就是元素自身宽度
  • height: 元素自身高度
  • left(x):元素开始位置到窗口左边的距离
  • right: 元素的右边到窗口左边的距离
  • bottom: 元素的下边到窗口上边的距离
  • top(y): 元素的上边到窗口上边的距离
  • x 和 y 相当于 left 和 top

示意图如下:

Vous guide étape par étape pour personnaliser une instruction de chargement différé dimage dans Vue2

该 API 的详细文档可以参照MDN - Element.getBoundingClientRect()

2.图片懒加载指令的基本介绍

2.1 最终的实现效果

最终效果如下图:

Vous guide étape par étape pour personnaliser une instruction de chargement différé dimage dans Vue2

2.2 图片懒加载指令的注册与使用

由于在个人博客系统中图片懒加载指令使用的比较频繁,使用我选择了全局注册该指令。

另外因为我使用事件总线这方法来自己通信,使用还需引入事件总线配置文件---eventBus.js

所以 main.js入口文件的代码如下:

import Vue from "vue";
import App from "./App.vue";
import "./eventBus"; //引入事件总线
import vLazy from "./directives/lazy";
Vue.directive("lazy", vLazy); //全局注册指令
new Vue({
  render: (h) => h(App),
}).$mount("#app");
Copier après la connexion
Copier après la connexion

使用 v-lazy 指令的示例代码如下:

<template>
  <div>
    <ul>
      <li>
        <img  alt="Vous guide étape par étape pour personnaliser une instruction de chargement différé d'image dans Vue2" >
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      imgs: [
        {
          id: "",
          src: "",
          alt: "",
          title: "",
        },
      ],
    };
  },
  //下面的代码可以用组件混入来进行封装,从而优化代码结构
  methods: {
    //触发mainScroll事件
    handleMainScroll() {
      this.$bus.$emit("mainScroll", this.$refs.container);
    },
  },
  mounted() {
    //监听滚轮事件
    this.$refs.container.addEventListener("scroll", this.handleMainScroll);
  },
  beforeDestroy() {
    this.$bus.$emit("mainScroll");//参数传入undefined,表示dom元素已经不存在
    //取消监听滚轮事件
    this.$refs.container.removeEventListener("scroll", this.handleMainScroll);
  },

};
</script>
Copier après la connexion

3. 实现图片懒加载的原理

要实现图片懒加载效果,我们首先要思考以下四个关键问题:

  • 如何监听容器的滚动条的滚动?

  • 使用自定义指令哪些钩子函数?

  • 如何判断图片 img 元素是否在用户的可见范围内?

  • 如何处理图片 img 元素的加载?

3.1 如何监听容器的滚动条的滚动?

对于这问题,由于我的博客系统在处理其他组件之间的传值问题时,使用了事件总线方法,所以为了方便,我也使用这一方法,当然大家可以针对实际场景使用其他方法来解决这问题。

所以我们要在 v-lazy 图片懒加载指令配置文件---lazy.js文件中监听事件总线 eventBus 中的mainScroll事件,同时为了性能优化,我们需要进行 mainScroll 事件的事件防抖

其中事件防抖工具函数---debounce.js代码如下:

/**
 * @param {Function} fn 需要进行防抖操作的事件函数
 * @param {Number} duration 间隔时间
 * @returns {Function} 已进行防抖的函数
 */
export default function (fn, duration = 100) {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, duration);
  };
}
Copier après la connexion

图片懒加载指令配置文件---lazy.js该部分代码如下:

import eventBus from "@/eventBus"; //引入事件总线
import { debounce } from "@/utils"; //引入函数防抖工具函数

// 调用setImages函数,就可以处理那些符合条件的图片
function setImages() {}

//监听事件总线中的mainScroll事件,该事件触发时调用setImages函数来加载图片
eventBus.$on("mainScroll", debounce(setImages, 50));
Copier après la connexion

3.2 使用自定义指令哪些钩子函数?

经过场景分析,我选用了insertedunbind这两个钩子函数,当 img 元素刚插入父节点时收集 img 的信息,并在内部使用一个 imgs 数组存储已收集到的信息,当指令与元素解绑时,进行 imgs 数组清空操作。

另外我们还需获取图片 img 元素的 DOM 节点和 src 属性值

  • 由于我们将指令绑定到了 img'元素上,所以可通过自定义指令钩子函树中的el参数得到其 DOM 节点
  • 由于我们将 src 值传给了指令,所以可通过bindings.value参数得到其 src 属性值

所以此时图片懒加载指令配置文件---lazy.js该部分代码如下:

import eventBus from "@/eventBus"; //引入事件总线
import { debounce } from "@/utils"; //引入函数防抖工具函数

// 调用setImages函数,就可以处理那些符合条件的图片
function setImages() {}

//监听事件总线中的mainScroll事件,该事件触发时调用setImages函数来加载图片
eventBus.$on("mainScroll", debounce(setImages, 50));

//上面代码是3.1 如何监听容器的滚动条的滚动?
//下面代码是3.2 使用自定义指令哪些钩子函数?

let imgs = []; //存储收集到的的图片信息 当图片加载好后删除该图片信息

//调用setImage函数,就可以进行单张图片的加载
function setImage(img) {}

export default {
  inserted(el, bindings) {
    //刚插入父节点时 收集img节点信息
    const img = {
      dom: el, //img 元素DOM节点
      src: bindings.value, //img的src属性值
    };
    imgs.push(img); //先将图片信息存储到imgs数组
    setImage(img); // 立即判断该图片是否要加载
  },
  unbind(el) {
    //解绑时 删除 imgs 中的所有图片信息
    imgs = imgs.filter((img) => img.dom !== el);
  },
};
Copier après la connexion

3.3 如何判断图片 img 元素是否在用户的可见范围内?

对于上面这问题,我们先进行问题拆分:

1、获得用户的可见范围(视口)

  • 由于我的博客系统只需考虑视口高度,所以我只使用了Element.clientHeight 这 API。(如果还需要考虑宽度就再使用Element.clientWidth)

2、获得图片 img 元素的位置信息

  • 我使用了Element.getBoundingClientRect()这 API。

3、判断图片 img 元素是否在视口内

  • img.getBoundingClientRect().top > 0 时,说明图片在视口内或视口下方
    • 当 img.getBoundingClientRect().top
    • 反之则不在视口内
  • img.getBoundingClientRect().top
  • 当-img.getBoundingClientRect().top
  • 反之则不在视口内

图片懒加载指令配置文件---lazy.js该部分代码如下:

import eventBus from "@/eventBus"; //引入事件总线
import { debounce } from "@/utils"; //引入函数防抖工具函数

let imgs = []; //存储收集到的的图片信息

// 调用setImages函数,就可以处理那些符合条件的图片
function setImages() {
  for (const img of imgs) {
    setImage(img); // 处理该图片
  }
}

//监听事件总线中的mainScroll事件,该事件触发时调用setImages函数来加载符合条件图片
eventBus.$on("mainScroll", debounce(setImages, 50));

//当图片加载好后删除该图片信息
export default {
  inserted(el, bindings) {
    //刚插入父节点时 收集img节点信息
    const img = {
      dom: el, //img 元素DOM节点
      src: bindings.value, //img的src属性值
    };
    imgs.push(img); //先将图片信息存储到imgs数组
    setImage(img); // 立即判断该图片是否要加载
  },
  unbind(el) {
    //解绑时 删除 imgs 中的所有图片信息
    imgs = imgs.filter((img) => img.dom !== el);
  },
};

//上面代码是3.1 如何监听容器的滚动条的滚动?+ 3.2 使用自定义指令哪些钩子函数?
//下面代码是3.3 如何判断图片 img 元素是否在用户的可见范围内?

//调用setImage函数,就可以进行单张图片的加载
function setImage(img) {
  const clientHeight = document.documentElement.clientHeight; //视口高度
  const rect = img.dom.getBoundingClientRect(); //图片的位置信息
  //取默认值150 是为了解决图片未加载成功时高度缺失的问题
  const height = rect.height || 150; //图片的高度

  // 判断该图片是否在视口范围内
  if (rect.top >= -height && rect.top <h3 data-id="heading-16"><strong>3.4 如何处理图片 img 元素的加载?</strong></h3><p>由效果图我们可看出一开始所有 img 元素都是一张默认的 GIF 图片---<code>defaultGif</code>,等该 img 元素进入到视口范围时,开始加载该图片,加载完成后再进行替换。</p><p>这里我还进行一个优化操作,就是先新建一个 <code>Image 对象实例</code>,代替 img 元素加载图片,因为图片加载完成后会触发<code>onload事件</code>,所以我们只需对<code>onload事件</code>进行改写,在其内部执行 img 元素的 src 属性替换操作,这样就解决了加载过程中图片空白的情况。</p><p>所以图片懒加载指令配置文件---<code>lazy.js</code>完整的代码如下:</p><pre class="brush:php;toolbar:false">import eventBus from "@/eventBus"; //引入事件总线
import { debounce } from "@/utils"; //引入函数防抖工具函数
import defaultGif from "@/assets/default.gif"; //在assets静态文件夹下放入默认图

let imgs = []; //存储收集到的且未加载的图片信息

//调用setImage函数,就可以进行单张图片的加载
function setImage(img) {
  img.dom.src = defaultGif; // 先暂时使用默认图片
  const clientHeight = document.documentElement.clientHeight; //视口高度
  const rect = img.dom.getBoundingClientRect(); //图片的位置信息
  //取默认值150 是为了解决图片未加载成功时 高度缺失的问题
  const height = rect.height || 150; //图片的高度
  // 判断该图片是否在视口范围内
  if (-rect.top  i !== img); //将已加载好的图片进行删除
  }
}

// 调用setImages函数,就可以处理那些符合条件的图片
function setImages() {
  for (const img of imgs) {
    setImage(img); // 处理该图片
  }
}

//监听事件总线中的mainScroll事件,该事件触发时调用setImages函数来加载符合条件图片
eventBus.$on("mainScroll", debounce(setImages, 50));

//当图片加载好后删除该图片信息
export default {
  inserted(el, bindings) {
    //刚插入父节点时 收集img节点信息
    const img = {
      dom: el, //img 元素DOM节点
      src: bindings.value, //img的src属性值
    };
    imgs.push(img); //先将图片信息存储到imgs数组
    setImage(img); // 立即判断该图片是否要加载
  },
  unbind(el) {
    //解绑时 清空 imgs
    imgs = imgs.filter((img) => img.dom !== el);
  },
};
Copier après la connexion

(学习视频分享:web前端开发编程基础视频

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal