Vue2自定义图片懒加载指令v-lazy如何实现长尾词优化?

2026-04-02 07:411阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计4433个文字,预计阅读时间需要18分钟。

Vue2自定义图片懒加载指令v-lazy如何实现长尾词优化?

目录+引言+1. 涉及到的主要内容+1.1 Vue2 中自定义指令+1.1.1 指令对象的钩子函数+1.1.2 钩子函数参数+1.2 使用事件总线进行模块间的通信+1.3 使用到的 Web API+1.3.1 Element.clientHeight+1.3.2 其他API

目录
  • 引言
  • 1.涉及到的主要知识讲解
    • 1.1 Vue2 中自定义指令
      • 1.1.1 指令对象的钩子函数
      • 1.1.2 钩子函数参数
    • 1.2 使用事件总线进行模块之间的通信
      • 1.3 使用到的 Web API
        • 1.3.1 Element.clientHeight
        • 1.3.2 Element.getBoundingClientRect()
    • 2.图片懒加载指令的基本介绍
      • 2.1 最终的实现效果
        • 2.2 图片懒加载指令的注册与使用
        • 3. 实现图片懒加载的原理
          • 3.1 如何监听容器的滚动条的滚动?
            • 3.2 使用自定义指令哪些钩子函数?
              • 3.3 如何判断图片 img 元素是否在用户的可见范围内?
                • 3.4 如何处理图片 img 元素的加载?

                引言

                由于我在开发的个人博客前台页面时,想优化网站的响应速度,所以想实现图片懒加载效果。

                我是通过自定义指令v-lazy实现的,所以在这跟大家分享一下这个指令的开发流程及其难点的解决方法。

                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

                Vue2自定义图片懒加载指令v-lazy如何实现长尾词优化?

                1.3.1 Element.clientHeight

                首先Element.clientHeight是一个只读属性,具有以下特点:

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

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

                示例图如下:

                该 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

                示意图如下:

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

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

                2.1 最终的实现效果

                最终效果如下图:

                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 class="container"> <ul> <li v-for="img in imgs" :key="img.id"> <img v-lazy="img.src" :alt="img.alt" :title="img.title" /> </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) =&gt; img.dom !== el); }, };

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

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

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

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

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

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

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

                img.getBoundingClientRect().top > 0 时,说明图片在视口内或视口下方

                • 当 img.getBoundingClientRect().top <= document.documentElement.clientHeight 时,该 img 元素在视口内
                • 反之则不在视口内

                img.getBoundingClientRect().top < 0 时,说明图片在视口内或视口上方

                • 当-img.getBoundingClientRect().top <= img.getBoundingClientRect().height 时,该 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 <= clientHeight) { // 在视口范围内 进行相关处理操作 } else { // 不在视口范围内 不进行操作 } }

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

                由效果图我们可看出一开始所有 img 元素都是一张默认的 GIF 图片---defaultGif,等该 img 元素进入到视口范围时,开始加载该图片,加载完成后再进行替换。

                这里我还进行一个优化操作,就是先新建一个 Image 对象实例,代替 img 元素加载图片,因为图片加载完成后会触发onload事件,所以我们只需对onload事件进行改写,在其内部执行 img 元素的 src 属性替换操作,这样就解决了加载过程中图片空白的情况。

                所以图片懒加载指令配置文件---lazy.js完整的代码如下:

                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 <= height && rect.top <= clientHeight) { // 在视口范围内 进行相关处理操作 const tempImg = new Image(); //新建Image对象实例 //改写onload事件 tempImg.onload = function () { // 当图片加载完成之后 img.dom.src = img.src; //替换img元素的src属性 }; tempImg.src = img.src; imgs = imgs.filter((i) => 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); }, };

                以上就是Vue2 中自定义图片懒加载指令 v-lazy实例详解的详细内容,更多关于Vue2自定义v-lazy指令的资料请关注易盾网络其它相关文章!

                本文共计4433个文字,预计阅读时间需要18分钟。

                Vue2自定义图片懒加载指令v-lazy如何实现长尾词优化?

                目录+引言+1. 涉及到的主要内容+1.1 Vue2 中自定义指令+1.1.1 指令对象的钩子函数+1.1.2 钩子函数参数+1.2 使用事件总线进行模块间的通信+1.3 使用到的 Web API+1.3.1 Element.clientHeight+1.3.2 其他API

                目录
                • 引言
                • 1.涉及到的主要知识讲解
                  • 1.1 Vue2 中自定义指令
                    • 1.1.1 指令对象的钩子函数
                    • 1.1.2 钩子函数参数
                  • 1.2 使用事件总线进行模块之间的通信
                    • 1.3 使用到的 Web API
                      • 1.3.1 Element.clientHeight
                      • 1.3.2 Element.getBoundingClientRect()
                  • 2.图片懒加载指令的基本介绍
                    • 2.1 最终的实现效果
                      • 2.2 图片懒加载指令的注册与使用
                      • 3. 实现图片懒加载的原理
                        • 3.1 如何监听容器的滚动条的滚动?
                          • 3.2 使用自定义指令哪些钩子函数?
                            • 3.3 如何判断图片 img 元素是否在用户的可见范围内?
                              • 3.4 如何处理图片 img 元素的加载?

                              引言

                              由于我在开发的个人博客前台页面时,想优化网站的响应速度,所以想实现图片懒加载效果。

                              我是通过自定义指令v-lazy实现的,所以在这跟大家分享一下这个指令的开发流程及其难点的解决方法。

                              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

                              Vue2自定义图片懒加载指令v-lazy如何实现长尾词优化?

                              1.3.1 Element.clientHeight

                              首先Element.clientHeight是一个只读属性,具有以下特点:

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

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

                              示例图如下:

                              该 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

                              示意图如下:

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

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

                              2.1 最终的实现效果

                              最终效果如下图:

                              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 class="container"> <ul> <li v-for="img in imgs" :key="img.id"> <img v-lazy="img.src" :alt="img.alt" :title="img.title" /> </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) =&gt; img.dom !== el); }, };

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

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

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

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

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

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

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

                              img.getBoundingClientRect().top > 0 时,说明图片在视口内或视口下方

                              • 当 img.getBoundingClientRect().top <= document.documentElement.clientHeight 时,该 img 元素在视口内
                              • 反之则不在视口内

                              img.getBoundingClientRect().top < 0 时,说明图片在视口内或视口上方

                              • 当-img.getBoundingClientRect().top <= img.getBoundingClientRect().height 时,该 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 <= clientHeight) { // 在视口范围内 进行相关处理操作 } else { // 不在视口范围内 不进行操作 } }

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

                              由效果图我们可看出一开始所有 img 元素都是一张默认的 GIF 图片---defaultGif,等该 img 元素进入到视口范围时,开始加载该图片,加载完成后再进行替换。

                              这里我还进行一个优化操作,就是先新建一个 Image 对象实例,代替 img 元素加载图片,因为图片加载完成后会触发onload事件,所以我们只需对onload事件进行改写,在其内部执行 img 元素的 src 属性替换操作,这样就解决了加载过程中图片空白的情况。

                              所以图片懒加载指令配置文件---lazy.js完整的代码如下:

                              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 <= height && rect.top <= clientHeight) { // 在视口范围内 进行相关处理操作 const tempImg = new Image(); //新建Image对象实例 //改写onload事件 tempImg.onload = function () { // 当图片加载完成之后 img.dom.src = img.src; //替换img元素的src属性 }; tempImg.src = img.src; imgs = imgs.filter((i) => 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); }, };

                              以上就是Vue2 中自定义图片懒加载指令 v-lazy实例详解的详细内容,更多关于Vue2自定义v-lazy指令的资料请关注易盾网络其它相关文章!