Vue2에서 이미지 지연 로딩 명령을 사용자 정의하는 방법을 단계별로 안내합니다.

青灯夜游
풀어 주다: 2022-09-22 20:29:48
앞으로
2169명이 탐색했습니다.

Vue에서 이미지 지연 로딩 지침을 사용자 정의하는 방법은 무엇입니까? 다음 기사에서는 Vue2의 사용자 정의 이미지 지연 로딩 명령 "v-lazy"에 대해 심층적으로 소개합니다. 도움이 되기를 바랍니다.

Vue2에서 이미지 지연 로딩 명령을 사용자 정의하는 방법을 단계별로 안내합니다.

개인 블로그의 프런트 엔드 페이지를 개발할 때 웹사이트의 응답 속도를 최적화하고 싶어서 이미지의 지연 로딩 효과를 구현하고 싶었습니다.

사용자 정의 명령 v-lazy를 통해 구현했는데, 이 명령의 개발 과정과 그 어려움에 대한 해결 방법을 여러분과 공유하고 싶습니다. [관련 권장사항: 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;
로그인 후 복사

1.3 使用到的 Web API

1.3.1 Element.clientHeight

首先Element.clientHeight]

1. 관련된 주요 지식에 대한 설명

🎜사용자 정의 이미지 지연 로딩 명령에는 주로 다음 세 가지 지식이 포함됩니다: 🎜
  • Vue2의 사용자 정의 명령
  • 모듈 간 통신을 위해 이벤트 버스 사용
  • 사용된 웹 API
    • Element.clientHeight
    • Element.getBoundingClientRect()
    • ul>
🎜이러한 지식 포인트를 아래에서 하나씩 소개하겠습니다. 🎜

🎜1.1 Vue2의 사용자 정의 지침🎜

🎜아래에서 사용자 정의 지침을 간략하게 소개하겠습니다. 자세한 소개는 Vue 공식 웹사이트-맞춤 지시어🎜. 🎜

🎜1.1.1 명령어 객체의 후크 기능🎜

  • bind: 명령어가 해당 요소에 바인딩될 때 한 번만 호출됩니다. 처음으로. 여기에서 일회성 초기화 설정을 수행할 수 있습니다.
  • 삽입됨: 바인딩된 요소가 상위 노드에 삽입될 때 호출됩니다(상위 노드만 존재한다고 보장되지만 반드시 문서에 삽입될 필요는 없습니다).
  • update: 구성 요소의 VNode가 업데이트될 때 호출되지만 해당 하위 VNode가 업데이트되기 전에 발생할 수도 있습니다. 지시어의 값은 변경되었을 수도 있고 변경되지 않았을 수도 있습니다. 불필요한 템플릿 업데이트는 업데이트 전과 후의 값을 비교하여 무시할 수 있습니다(아래 후크 기능 매개변수에 대한 자세한 내용 참조).
  • comComponentUpdated: 명령이 있는 구성 요소의 모든 VNode와 해당 하위 VNode가 업데이트된 후에 호출됩니다.
  • unbind: 명령어가 요소에서 바인딩 해제될 때 한 번만 호출됩니다.
🎜후크 기능의 매개변수에는 주로 el, 바인딩, vnode, oldVnode 네 가지가 포함됩니다. 🎜

🎜1.1.2 Hook 함수 매개변수🎜

  • el: 명령어에 바인딩된 요소를 사용하여 DOM을 직접 작동할 수 있습니다.
  • 바인딩: 다음 속성을 포함하는 개체:
    • name: v- 접두사를 제외한 지시문의 이름.
    • 값: v-my-directive="1 + 1"과 같은 지시문의 바인딩 값, 바인딩 값은 2입니다.
    • oldValue: 명령 바인딩의 이전 값이며 업데이트 및 구성 요소 업데이트 후크에서만 사용할 수 있습니다. 값이 변경되었는지 여부에 관계없이 사용할 수 있습니다.
    • 표현식: 문자열 형식의 명령어 표현입니다. 예를 들어 v-my-directive="1 + 1"에서 표현식은 "1 + 1"입니다.
    • arg: 명령에 전달되는 매개변수, 선택사항. 예를 들어 v-my-directive:foo에서 매개변수는 "foo"입니다.
    • 수정자: 수정자를 포함하는 개체입니다. 예를 들어 v-my-directive.foo.bar에서 수정자 개체는 { foo: true, bar: true }입니다.
  • vnode: Vue 컴파일로 생성된 가상 노드입니다. 자세한 내용을 알아보려면 VNode API로 이동하세요.
  • oldVnode: 이전 가상 노드이며 업데이트 및 구성 요소 업데이트 후크에서만 사용할 수 있습니다.

🎜1.2 모듈 간 통신에 이벤트 버스 사용🎜

🎜이벤트 버스에 익숙하지 않은 친구는 이 블로그를 참고하세요Vue 이벤트 버스(EventBus)란 무엇입니까🎜. 🎜
  • 이벤트 버스에서 이벤트 수신---$on 메서드 호출
  • 이벤트 버스에서 이벤트 트리거---$emit 메서드 호출
  • 버스에서 이벤트 듣기 --- $off 메소드 호출
🎜 vue 예제를 사용하여 이벤트 버스를 구현하거나 직접 캡슐화할 수 있습니다. 첫 번째 메소드를 사용했습니다. 🎜🎜그래서 이벤트 버스 구성 파일 ---eventBus.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");
로그인 후 복사
로그인 후 복사

🎜1.3 사용된 웹 API🎜 h3>

🎜1.3.1 Element.clientHeight🎜

🎜우선 Element.clientHeight는 🎜읽기 전용 속성🎜입니다. 다음 특성: 🎜
  • 对于那些没有定义 CSS 或者内联布局盒子的元素,该 API 会返回 0;
  • 对于根元素(html 元素)或怪异模式下的 body 元素,该 API 将返回视口高度(不包含任何滚动条)
  • 其他情况,该 API 会返回元素内部的高度(以像素为单位),包含contentpadding,不包含bordermargin与水平滚动条(如果存在)。

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

示例图如下:

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

示意图如下:

Vue2에서 이미지 지연 로딩 명령을 사용자 정의하는 방법을 단계별로 안내합니다.

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

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

2.1 最终的实现效果

最终效果如下图:

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");
로그인 후 복사
로그인 후 복사

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

<template>
  <div>
    <ul>
      <li>
        <img  alt="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>
로그인 후 복사

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前端开发编程基础视频

위 내용은 Vue2에서 이미지 지연 로딩 명령을 사용자 정의하는 방법을 단계별로 안내합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿