Home >Web Front-end >Vue.js >Take you step by step to customize an image lazy loading instruction in Vue2
How to customize the image lazy loading instruction in
Vue? The following article will give you an in-depth introduction to the custom image lazy loading instruction "v-lazy" in Vue2. I hope it will be helpful to you!
When I was developing the front-end page of my personal blog, I wanted to optimize the response speed of the website, so I wanted to achieve the lazy loading effect of images.
I implemented it through the custom command v-lazy
, so I would like to share with you the development process of this command and the solutions to its difficulties. [Related recommendations: vuejs video tutorial]
The custom image lazy loading instructions mainly involve the following three Block knowledge:
I will introduce these knowledge points one by one below.
I will only briefly introduce the custom instructions below. For detailed introduction, you can refer toVue official website-custom instructions .
The parameters of the hook function mainly include these four el, binding, vnode, oldVnode
.
Friends who are not familiar with the event bus can refer to this blogWhat is a Vue event Bus(EventBus).
We can use the vue example to implement the event bus, or we can encapsulate it ourselves; I used the first method.
So the code of the event bus configuration file ---eventBus.js
is as follows:
import Vue from "vue"; const eventBus = new Vue({}); /* * 事件名:mainScroll * 含义:主区域滚动条位置变化后触发 * 参数: * - 滚动的dom元素,如果是undefined,则表示dom元素已经不存在 */ //在Vue.prototype原型上注册事件总线,方便vue实例对象监听和触发 Vue.prototype.$bus = eventBus; //导出事件总线,方便在其他js模块监听和触发事件总线上的事件 export default eventBus;
First of all Element.clientHeight
is a read-only property with the following characteristics:
content
与padding
,不包含border
、margin
与水平滚动条(如果存在)。另外改 API 会将获取的值四舍五入取整数。如果你需要小数结果,可以使用 element.getBoundingClientRect()方法。
示例图如下:
该 API 的详细文档可参照MDN - Element.clientHeight。
Element.getBoundingClientRect()
方法返回一个DOMRect
对象,其提供了元素的大小及其相对于视口的位置。
该方法无参数,返回值为DOMRect
对象,该对象的属性以下几个:
示意图如下:
该 API 的详细文档可以参照MDN - Element.getBoundingClientRect()
最终效果如下图:
由于在个人博客系统中图片懒加载指令使用的比较频繁,使用我选择了全局注册该指令。
另外因为我使用事件总线这方法来自己通信,使用还需引入事件总线配置文件---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="Take you step by step to customize an image lazy loading instruction in 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>
要实现图片懒加载效果,我们首先要思考以下四个关键问题:
如何监听容器的滚动条的滚动?
使用自定义指令哪些钩子函数?
如何判断图片 img 元素是否在用户的可见范围内?
如何处理图片 img 元素的加载?
对于这问题,由于我的博客系统在处理其他组件之间的传值问题时,使用了事件总线方法,所以为了方便,我也使用这一方法,当然大家可以针对实际场景使用其他方法来解决这问题。
所以我们要在 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));
经过场景分析,我选用了inserted
和unbind
这两个钩子函数,当 img 元素刚插入父节点时收集 img 的信息,并在内部使用一个 imgs 数组存储已收集到的信息,当指令与元素解绑时,进行 imgs 数组清空操作。
另外我们还需获取图片 img 元素的 DOM 节点和 src 属性值
el参数
得到其 DOM 节点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); }, };
对于上面这问题,我们先进行问题拆分:
1、获得用户的可见范围(视口)
Element.clientHeight
这 API。(如果还需要考虑宽度就再使用Element.clientWidth)2、获得图片 img 元素的位置信息
Element.getBoundingClientRect()
这 API。3、判断图片 img 元素是否在视口内
图片懒加载指令配置文件---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); }, };
The above is the detailed content of Take you step by step to customize an image lazy loading instruction in Vue2. For more information, please follow other related articles on the PHP Chinese website!