Heim  >  Artikel  >  Web-Frontend  >  Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

青灯夜游
青灯夜游nach vorne
2022-09-22 20:29:482154Durchsuche

Wie kann ich die Anweisung zum verzögerten Laden von Bildern in

Vue anpassen? Der folgende Artikel gibt Ihnen eine ausführliche Einführung in die benutzerdefinierte Bild-Lazy-Loading-Anweisung „v-lazy“ in Vue2. Ich hoffe, er wird Ihnen hilfreich sein!

Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

Als ich die Front-End-Seite meines persönlichen Blogs entwickelte, wollte ich die Reaktionsgeschwindigkeit der Website optimieren, also wollte ich den Lazy-Loading-Effekt von Bildern erzielen.

Ich habe es über den benutzerdefinierten Befehl v-lazy implementiert, daher möchte ich Ihnen den Entwicklungsprozess dieses Befehls und die Lösungen für seine Schwierigkeiten mitteilen. [Verwandte Empfehlungen: vuejs Video-Tutorial自定义指令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;

1.3 使用到的 Web API

1.3.1 Element.clientHeight

首先Element.clientHeight]

1. Erläuterung der wichtigsten Kenntnisse

🎜Die benutzerdefinierte Bild-Lazy-Loading-Anweisung umfasst hauptsächlich die folgenden drei Wissensteile: 🎜
  • Benutzerdefinierte Anweisungen in Vue2
  • Verwenden Sie den Ereignisbus für die Kommunikation zwischen Modulen
  • Verwendete Web-API
    • Element.clientHeight
    • Element.getBoundingClientRect()
    • ul>
🎜Ich werde diese Wissenspunkte im Folgenden einzeln vorstellen. 🎜

🎜1.1 Benutzerdefinierte Anweisungen in Vue2🎜

🎜Ich werde die benutzerdefinierten Anweisungen unten nur kurz vorstellen. Eine detaillierte Einführung finden Sie unter Vue offizielle Website-benutzerdefinierte Richtlinie🎜. 🎜

🎜1.1.1 Hook-Funktion des Anweisungsobjekts🎜

  • bind: wird nur einmal aufgerufen, wenn die Anweisung an das Element für gebunden ist Zum ersten Mal. Hier können einmalige Initialisierungseinstellungen vorgenommen werden.
  • Eingefügt: Wird aufgerufen, wenn das gebundene Element in den übergeordneten Knoten eingefügt wird (nur der übergeordnete Knoten ist garantiert vorhanden, wird aber nicht unbedingt in das Dokument eingefügt).
  • Aktualisierung: Wird aufgerufen, wenn der VNode der Komponente aktualisiert wird, kann jedoch erfolgen, bevor der untergeordnete VNode aktualisiert wird. Der Wert der Richtlinie kann sich geändert haben oder auch nicht. Unnötige Vorlagenaktualisierungen können ignoriert werden, indem die Werte vor und nach der Aktualisierung verglichen werden (siehe Details zu Hook-Funktionsparametern unten).
  • componentUpdated: Wird aufgerufen, nachdem alle VNodes der Komponente, in der sich die Anweisung befindet, und ihre Unter-VNodes aktualisiert wurden.
  • unbind: Wird nur einmal aufgerufen, wenn die Bindung der Anweisung vom Element gelöst wird.
🎜Die Parameter der Hook-Funktion umfassen hauptsächlich diese vier el, binding, vnode, oldVnode. 🎜

🎜1.1.2 Hook-Funktionsparameter🎜

  • el: Das an die Anweisung gebundene Element kann zum direkten Betrieb des DOM verwendet werden.
  • Bindung: ein Objekt, das die folgenden Eigenschaften enthält:
    • Name: der Name der Direktive, ohne das Präfix v-.
    • Wert: Der Bindungswert der Direktive, zum Beispiel: v-my-directive="1 + 1", der Bindungswert ist 2.
    • oldValue: Der vorherige Wert der Anweisungsbindung, nur in Update- und ComponentUpdated-Hooks verfügbar. Verfügbar unabhängig davon, ob sich der Wert geändert hat.
    • Ausdruck: Anweisungsausdruck in Stringform. In v-my-directive="1 + 1" lautet der Ausdruck beispielsweise „1 + 1“.
    • arg: An den Befehl übergebene Parameter, optional. In v-my-directive:foo lautet der Parameter beispielsweise „foo“.
    • Modifikatoren: ein Objekt, das Modifikatoren enthält. Beispiel: In v-my-directive.foo.bar ist das Modifikatorobjekt { foo: true, bar: true }.
  • vnode: Der durch die Vue-Kompilierung generierte virtuelle Knoten. Wechseln Sie zur VNode-API, um weitere Details zu erfahren.
  • oldVnode: Der vorherige virtuelle Knoten, nur in den Hooks „update“ und „componentUpdated“ verfügbar.

🎜1.2 Verwenden Sie den Event-Bus für die Kommunikation zwischen Modulen🎜

🎜Freunde, die mit Event-Bus nicht vertraut sind, können auf diesen Blog verweisenWas ist Vue Event Bus (EventBus)🎜. 🎜
  • Ereignisse auf dem Ereignisbus abhören – die Methode $on aufrufen
  • Ereignisse auf dem Ereignisbus auslösen – die Methode $emit aufrufen
  • Abhören von Ereignissen im Bus --- Rufen Sie die $off-Methode auf
🎜Wir können das Vue-Beispiel verwenden, um den Ereignisbus zu implementieren, oder wir können ihn selbst kapseln. Ich habe die erste Methode verwendet. 🎜🎜Der Code der Event-Bus-Konfigurationsdatei ---eventBus.js lautet also wie folgt: 🎜
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");

🎜1.3 Web API verwendet🎜 h3>

🎜1.3.1 Element.clientHeight🎜

🎜Zuallererst ist Element.clientHeight eine 🎜schreibgeschützte Eigenschaft🎜 mit die folgenden Eigenschaften: 🎜
  • 对于那些没有定义 CSS 或者内联布局盒子的元素,该 API 会返回 0;
  • 对于根元素(html 元素)或怪异模式下的 body 元素,该 API 将返回视口高度(不包含任何滚动条)
  • 其他情况,该 API 会返回元素内部的高度(以像素为单位),包含contentpadding,不包含bordermargin与水平滚动条(如果存在)。

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

示例图如下:

Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

该 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

示意图如下:

Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

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

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

2.1 最终的实现效果

最终效果如下图:

Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen

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");

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

<template>
  <div>
    <ul>
      <li>
        <img  alt="Führen Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen" >
      </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>

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);
  };
}

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

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

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

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

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);
  },
};

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);
  },
};

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

Das obige ist der detaillierte Inhalt vonFühren Sie Sie Schritt für Schritt durch, um eine Anweisung zum verzögerten Laden von Bildern in Vue2 anzupassen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen