最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue2?中自定義圖片懶加載指令?v-lazy實(shí)例詳解

 更新時(shí)間:2022年09月22日 10:32:03   作者:forwardXX  
這篇文章主要為大家介紹了Vue2?中自定義圖片懶加載指令?v-lazy實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

由于我在開發(fā)的個(gè)人博客前臺(tái)頁面時(shí),想優(yōu)化網(wǎng)站的響應(yīng)速度,所以想實(shí)現(xiàn)圖片懶加載效果。

我是通過自定義指令v-lazy實(shí)現(xiàn)的,所以在這跟大家分享一下這個(gè)指令的開發(fā)流程及其難點(diǎn)的解決方法。

1.涉及到的主要知識(shí)講解

自定義圖片懶加載指令主要涉及以下三塊知識(shí):

  • Vue2 中自定義指令
  • 使用事件總線進(jìn)行模塊之間的通信
  • 使用到的 Web API
    • Element.clientHeight
    • Element.getBoundingClientRect()

下面我會(huì)對(duì)這些知識(shí)點(diǎn)進(jìn)行一一介紹。

1.1 Vue2 中自定義指令

下面我只對(duì)自定義指令做簡單的介紹,詳細(xì)介紹大家可以參照Vue 官網(wǎng) - 自定義指令。

1.1.1 指令對(duì)象的鉤子函數(shù)

  • bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。
  • inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
  • update:所在組件的 VNode 更新時(shí)調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒有。可通過比較更新前后的值來忽略不必要的模板更新 (詳細(xì)的鉤子函數(shù)參數(shù)見下)。
  • componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。
  • unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用。

鉤子函數(shù)的參數(shù)主要有這四個(gè)el、binding、vnode、oldVnode。

1.1.2 鉤子函數(shù)參數(shù)

  • el:指令所綁定的元素,可以用來直接操作 DOM。
  • binding:一個(gè)對(duì)象,包含以下 property:
    • name:指令名,不包括 v- 前綴。
    • value:指令的綁定值,如:v-my-directive="1 + 1" 中,綁定值為 2。
    • oldValue:指令綁定的前一個(gè)值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
    • expression:字符串形式的指令表達(dá)式。如 v-my-directive="1 + 1" 中,表達(dá)式為 "1 + 1"。
    • arg:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo"。
    • modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar 中,修飾符對(duì)象為 { foo: true, bar: true }。
  • vnode:Vue 編譯生成的虛擬節(jié)點(diǎn)。移步 VNode API 來了解更多詳情。
  • oldVnode:上一個(gè)虛擬節(jié)點(diǎn),僅在 update 和 componentUpdated 鉤子中可用。

1.2 使用事件總線進(jìn)行模塊之間的通信

對(duì)事件總線不熟悉的朋友,可以參照該博客什么是 Vue 事件總線(EventBus)。

  • 監(jiān)聽事件總線上的事件---調(diào)用 $on 方法
  • 觸發(fā)事件總線上的事件---調(diào)用 $emit 方法
  • 取消監(jiān)聽事件總線上的事件---調(diào)用 $off 方法

我們可以借助 vue 示例來實(shí)現(xiàn)事件總線,也可以自行封裝;我使用了第一種方法。

因此事件總線配置文件---eventBus.js 的代碼如下:

import Vue from "vue";
const eventBus = new Vue({});
/*
 * 事件名:mainScroll
 * 含義:主區(qū)域滾動(dòng)條位置變化后觸發(fā)
 * 參數(shù):
 * - 滾動(dòng)的dom元素,如果是undefined,則表示dom元素已經(jīng)不存在
 */
//在Vue.prototype原型上注冊(cè)事件總線,方便vue實(shí)例對(duì)象監(jiān)聽和觸發(fā)
Vue.prototype.$bus = eventBus;
//導(dǎo)出事件總線,方便在其他js模塊監(jiān)聽和觸發(fā)事件總線上的事件
export default eventBus;

1.3 使用到的 Web API

1.3.1 Element.clientHeight

首先Element.clientHeight是一個(gè)只讀屬性,具有以下特點(diǎn):

  • 對(duì)于那些沒有定義 CSS 或者內(nèi)聯(lián)布局盒子的元素,該 API 會(huì)返回 0;
  • 對(duì)于根元素(html 元素)或怪異模式下的 body 元素,該 API 將返回視口高度(不包含任何滾動(dòng)條)
  • 其他情況,該 API 會(huì)返回元素內(nèi)部的高度(以像素為單位),包含contentpadding,不包含border、margin與水平滾動(dòng)條(如果存在)。

另外改 API 會(huì)將獲取的值四舍五入取整數(shù)。如果你需要小數(shù)結(jié)果,可以使用 element.getBoundingClientRect()方法。

示例圖如下:

該 API 的詳細(xì)文檔可參照MDN - Element.clientHeight。

1.3.2 Element.getBoundingClientRect()

Element.getBoundingClientRect()方法返回一個(gè)DOMRect對(duì)象,其提供了元素的大小及其相對(duì)于視口的位置。 該方法無參數(shù),返回值為DOMRect對(duì)象,該對(duì)象的屬性以下幾個(gè):

  • width:就是元素自身寬度
  • height: 元素自身高度
  • left(x):元素開始位置到窗口左邊的距離
  • right: 元素的右邊到窗口左邊的距離
  • bottom: 元素的下邊到窗口上邊的距離
  • top(y): 元素的上邊到窗口上邊的距離
  • x 和 y 相當(dāng)于 left 和 top

示意圖如下:

該 API 的詳細(xì)文檔可以參照MDN - Element.getBoundingClientRect()

2.圖片懶加載指令的基本介紹

2.1 最終的實(shí)現(xiàn)效果

最終效果如下圖:

2.2 圖片懶加載指令的注冊(cè)與使用

由于在個(gè)人博客系統(tǒng)中圖片懶加載指令使用的比較頻繁,使用我選擇了全局注冊(cè)該指令。

另外因?yàn)槲沂褂檬录偩€這方法來自己通信,使用還需引入事件總線配置文件---eventBus.js

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

import Vue from "vue";
import App from "./App.vue";
import "./eventBus"; //引入事件總線
import vLazy from "./directives/lazy";
Vue.directive("lazy", vLazy); //全局注冊(cè)指令
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: "",
        },
      ],
    };
  },
  //下面的代碼可以用組件混入來進(jìn)行封裝,從而優(yōu)化代碼結(jié)構(gòu)
  methods: {
    //觸發(fā)mainScroll事件
    handleMainScroll() {
      this.$bus.$emit("mainScroll", this.$refs.container);
    },
  },
  mounted() {
    //監(jiān)聽滾輪事件
    this.$refs.container.addEventListener("scroll", this.handleMainScroll);
  },
  beforeDestroy() {
    this.$bus.$emit("mainScroll");//參數(shù)傳入undefined,表示dom元素已經(jīng)不存在
    //取消監(jiān)聽滾輪事件
    this.$refs.container.removeEventListener("scroll", this.handleMainScroll);
  },
};
</script>

3. 實(shí)現(xiàn)圖片懶加載的原理

要實(shí)現(xiàn)圖片懶加載效果,我們首先要思考以下四個(gè)關(guān)鍵問題:

  • 如何監(jiān)聽容器的滾動(dòng)條的滾動(dòng)?
  • 使用自定義指令哪些鉤子函數(shù)?
  • 如何判斷圖片 img 元素是否在用戶的可見范圍內(nèi)?
  • 如何處理圖片 img 元素的加載?

3.1 如何監(jiān)聽容器的滾動(dòng)條的滾動(dòng)?

對(duì)于這問題,由于我的博客系統(tǒng)在處理其他組件之間的傳值問題時(shí),使用了事件總線方法,所以為了方便,我也使用這一方法,當(dāng)然大家可以針對(duì)實(shí)際場景使用其他方法來解決這問題。

所以我們要在 v-lazy 圖片懶加載指令配置文件---lazy.js文件中監(jiān)聽事件總線 eventBus 中的mainScroll事件,同時(shí)為了性能優(yōu)化,我們需要進(jìn)行 mainScroll 事件的事件防抖。

其中事件防抖工具函數(shù)---debounce.js代碼如下:

/**
 * @param {Function} fn 需要進(jìn)行防抖操作的事件函數(shù)
 * @param {Number} duration 間隔時(shí)間
 * @returns {Function} 已進(jìn)行防抖的函數(shù)
 */
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"; //引入函數(shù)防抖工具函數(shù)
// 調(diào)用setImages函數(shù),就可以處理那些符合條件的圖片
function setImages() {}
//監(jiān)聽事件總線中的mainScroll事件,該事件觸發(fā)時(shí)調(diào)用setImages函數(shù)來加載圖片
eventBus.$on("mainScroll", debounce(setImages, 50));

3.2 使用自定義指令哪些鉤子函數(shù)?

經(jīng)過場景分析,我選用了insertedunbind這兩個(gè)鉤子函數(shù),當(dāng) img 元素剛插入父節(jié)點(diǎn)時(shí)收集 img 的信息,并在內(nèi)部使用一個(gè) imgs 數(shù)組存儲(chǔ)已收集到的信息,當(dāng)指令與元素解綁時(shí),進(jìn)行 imgs 數(shù)組清空操作。

另外我們還需獲取圖片 img 元素的 DOM 節(jié)點(diǎn)和 src 屬性值

  • 由于我們將指令綁定到了 img'元素上,所以可通過自定義指令鉤子函樹中的el參數(shù)得到其 DOM 節(jié)點(diǎn)
  • 由于我們將 src 值傳給了指令,所以可通過bindings.value參數(shù)得到其 src 屬性值

所以此時(shí)圖片懶加載指令配置文件---lazy.js該部分代碼如下:

import eventBus from "@/eventBus"; //引入事件總線
import { debounce } from "@/utils"; //引入函數(shù)防抖工具函數(shù)
// 調(diào)用setImages函數(shù),就可以處理那些符合條件的圖片
function setImages() {}
//監(jiān)聽事件總線中的mainScroll事件,該事件觸發(fā)時(shí)調(diào)用setImages函數(shù)來加載圖片
eventBus.$on("mainScroll", debounce(setImages, 50));
//上面代碼是3.1 如何監(jiān)聽容器的滾動(dòng)條的滾動(dòng)?
//下面代碼是3.2 使用自定義指令哪些鉤子函數(shù)?
let imgs = []; //存儲(chǔ)收集到的的圖片信息 當(dāng)圖片加載好后刪除該圖片信息
//調(diào)用setImage函數(shù),就可以進(jìn)行單張圖片的加載
function setImage(img) {}
export default {
  inserted(el, bindings) {
    //剛插入父節(jié)點(diǎn)時(shí) 收集img節(jié)點(diǎn)信息
    const img = {
      dom: el, //img 元素DOM節(jié)點(diǎn)
      src: bindings.value, //img的src屬性值
    };
    imgs.push(img); //先將圖片信息存儲(chǔ)到imgs數(shù)組
    setImage(img); // 立即判斷該圖片是否要加載
  },
  unbind(el) {
    //解綁時(shí) 刪除 imgs 中的所有圖片信息
    imgs = imgs.filter((img) =&gt; img.dom !== el);
  },
};

3.3 如何判斷圖片 img 元素是否在用戶的可見范圍內(nèi)?

對(duì)于上面這問題,我們先進(jìn)行問題拆分:

  • 獲得用戶的可見范圍(視口)

由于我的博客系統(tǒng)只需考慮視口高度,所以我只使用了Element.clientHeight 這 API。(如果還需要考慮寬度就再使用Element.clientWidth)

  • 獲得圖片 img 元素的位置信息

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

  • 判斷圖片 img 元素是否在視口內(nèi)

img.getBoundingClientRect().top > 0 時(shí),說明圖片在視口內(nèi)或視口下方

  • 當(dāng) img.getBoundingClientRect().top <= document.documentElement.clientHeight 時(shí),該 img 元素在視口內(nèi)
  • 反之則不在視口內(nèi)

img.getBoundingClientRect().top < 0 時(shí),說明圖片在視口內(nèi)或視口上方

  • 當(dāng)-img.getBoundingClientRect().top <= img.getBoundingClientRect().height 時(shí),該 img 元素在視口內(nèi)
  • 反之則不在視口內(nèi)

圖片懶加載指令配置文件---lazy.js該部分代碼如下:

import eventBus from "@/eventBus"; //引入事件總線
import { debounce } from "@/utils"; //引入函數(shù)防抖工具函數(shù)
let imgs = []; //存儲(chǔ)收集到的的圖片信息
// 調(diào)用setImages函數(shù),就可以處理那些符合條件的圖片
function setImages() {
  for (const img of imgs) {
    setImage(img); // 處理該圖片
  }
}
//監(jiān)聽事件總線中的mainScroll事件,該事件觸發(fā)時(shí)調(diào)用setImages函數(shù)來加載符合條件圖片
eventBus.$on("mainScroll", debounce(setImages, 50));
//當(dāng)圖片加載好后刪除該圖片信息
export default {
  inserted(el, bindings) {
    //剛插入父節(jié)點(diǎn)時(shí) 收集img節(jié)點(diǎn)信息
    const img = {
      dom: el, //img 元素DOM節(jié)點(diǎn)
      src: bindings.value, //img的src屬性值
    };
    imgs.push(img); //先將圖片信息存儲(chǔ)到imgs數(shù)組
    setImage(img); // 立即判斷該圖片是否要加載
  },
  unbind(el) {
    //解綁時(shí) 刪除 imgs 中的所有圖片信息
    imgs = imgs.filter((img) => img.dom !== el);
  },
};
//上面代碼是3.1 如何監(jiān)聽容器的滾動(dòng)條的滾動(dòng)?+ 3.2 使用自定義指令哪些鉤子函數(shù)?
//下面代碼是3.3 如何判斷圖片 img 元素是否在用戶的可見范圍內(nèi)?
//調(diào)用setImage函數(shù),就可以進(jìn)行單張圖片的加載
function setImage(img) {
  const clientHeight = document.documentElement.clientHeight; //視口高度
  const rect = img.dom.getBoundingClientRect(); //圖片的位置信息
  //取默認(rèn)值150 是為了解決圖片未加載成功時(shí)高度缺失的問題
  const height = rect.height || 150; //圖片的高度
  // 判斷該圖片是否在視口范圍內(nèi)
  if (rect.top >= -height && rect.top <= clientHeight) {
    // 在視口范圍內(nèi) 進(jìn)行相關(guān)處理操作
  } else {
    // 不在視口范圍內(nèi) 不進(jìn)行操作
  }
}

3.4 如何處理圖片 img 元素的加載?

由效果圖我們可看出一開始所有 img 元素都是一張默認(rèn)的 GIF 圖片---defaultGif,等該 img 元素進(jìn)入到視口范圍時(shí),開始加載該圖片,加載完成后再進(jìn)行替換。

這里我還進(jìn)行一個(gè)優(yōu)化操作,就是先新建一個(gè) Image 對(duì)象實(shí)例,代替 img 元素加載圖片,因?yàn)閳D片加載完成后會(huì)觸發(fā)onload事件,所以我們只需對(duì)onload事件進(jìn)行改寫,在其內(nèi)部執(zhí)行 img 元素的 src 屬性替換操作,這樣就解決了加載過程中圖片空白的情況。

所以圖片懶加載指令配置文件---lazy.js完整的代碼如下:

import eventBus from "@/eventBus"; //引入事件總線
import { debounce } from "@/utils"; //引入函數(shù)防抖工具函數(shù)
import defaultGif from "@/assets/default.gif"; //在assets靜態(tài)文件夾下放入默認(rèn)圖
let imgs = []; //存儲(chǔ)收集到的且未加載的圖片信息
//調(diào)用setImage函數(shù),就可以進(jìn)行單張圖片的加載
function setImage(img) {
  img.dom.src = defaultGif; // 先暫時(shí)使用默認(rèn)圖片
  const clientHeight = document.documentElement.clientHeight; //視口高度
  const rect = img.dom.getBoundingClientRect(); //圖片的位置信息
  //取默認(rèn)值150 是為了解決圖片未加載成功時(shí) 高度缺失的問題
  const height = rect.height || 150; //圖片的高度
  // 判斷該圖片是否在視口范圍內(nèi)
  if (-rect.top <= height && rect.top <= clientHeight) {
    // 在視口范圍內(nèi) 進(jìn)行相關(guān)處理操作
    const tempImg = new Image(); //新建Image對(duì)象實(shí)例
    //改寫onload事件
    tempImg.onload = function () {
      // 當(dāng)圖片加載完成之后
      img.dom.src = img.src; //替換img元素的src屬性
    };
    tempImg.src = img.src;
    imgs = imgs.filter((i) => i !== img); //將已加載好的圖片進(jìn)行刪除
  }
}
// 調(diào)用setImages函數(shù),就可以處理那些符合條件的圖片
function setImages() {
  for (const img of imgs) {
    setImage(img); // 處理該圖片
  }
}
//監(jiān)聽事件總線中的mainScroll事件,該事件觸發(fā)時(shí)調(diào)用setImages函數(shù)來加載符合條件圖片
eventBus.$on("mainScroll", debounce(setImages, 50));
//當(dāng)圖片加載好后刪除該圖片信息
export default {
  inserted(el, bindings) {
    //剛插入父節(jié)點(diǎn)時(shí) 收集img節(jié)點(diǎn)信息
    const img = {
      dom: el, //img 元素DOM節(jié)點(diǎn)
      src: bindings.value, //img的src屬性值
    };
    imgs.push(img); //先將圖片信息存儲(chǔ)到imgs數(shù)組
    setImage(img); // 立即判斷該圖片是否要加載
  },
  unbind(el) {
    //解綁時(shí) 清空 imgs
    imgs = imgs.filter((img) => img.dom !== el);
  },
};

以上就是Vue2 中自定義圖片懶加載指令 v-lazy實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue2自定義v-lazy指令的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue中的vue-print-nb如何實(shí)現(xiàn)頁面打印

    vue中的vue-print-nb如何實(shí)現(xiàn)頁面打印

    這篇文章主要介紹了vue中的vue-print-nb如何實(shí)現(xiàn)頁面打印,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue-amap安裝和用法步驟

    vue-amap安裝和用法步驟

    vue-amap是餓了么開源的一套基于?Vue?2.0?和高德地圖的地圖組件。接下來通過本文給大家介紹vue-amap安裝和使用,需要的朋友可以參考下
    2021-12-12
  • vue3.0 加載json的方法(非ajax)

    vue3.0 加載json的方法(非ajax)

    這篇文章主要介紹了vue3.0 加載json的方法(非ajax),幫助大家更好的理解和學(xué)習(xí)vue,感興趣的朋友可以了解下
    2020-10-10
  • 關(guān)于vue混入(mixin)的解讀

    關(guān)于vue混入(mixin)的解讀

    這篇文章主要介紹了關(guān)于vue混入(mixin)的解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue實(shí)現(xiàn)原生下拉刷新

    vue實(shí)現(xiàn)原生下拉刷新

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)原生下拉刷新,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 關(guān)于在vue2中使用weixin-js-sdk的詳細(xì)步驟

    關(guān)于在vue2中使用weixin-js-sdk的詳細(xì)步驟

    公司最近有微信公眾號(hào)的需求,那么微信登錄授權(quán)和如何使用WX-JSSDk實(shí)現(xiàn)分享等等肯定是最頭疼的問題,這篇文章主要給大家介紹了關(guān)于在vue2中使用weixin-js-sdk的詳細(xì)步驟,需要的朋友可以參考下
    2024-07-07
  • 詳解Vue3 Teleport 的實(shí)踐及原理

    詳解Vue3 Teleport 的實(shí)踐及原理

    這篇文章主要介紹了Vue3 Teleport 組件的實(shí)踐及原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Vue利用axios發(fā)送請(qǐng)求并代理請(qǐng)求的實(shí)現(xiàn)代碼

    Vue利用axios發(fā)送請(qǐng)求并代理請(qǐng)求的實(shí)現(xiàn)代碼

    在Web開發(fā)中,跨域問題是一個(gè)常見難題,通常由瀏覽器的同源策略引起,通過簡單配置vue.config.js文件,以及安裝axios依賴,即可實(shí)現(xiàn)前后端的無縫連接和數(shù)據(jù)交換,這種方法簡便有效,對(duì)于處理跨域請(qǐng)求問題非常實(shí)用
    2024-10-10
  • vue使用transition組件動(dòng)畫效果的實(shí)例代碼

    vue使用transition組件動(dòng)畫效果的實(shí)例代碼

    這篇文章主要介紹了vue使用transition組件動(dòng)畫效果的實(shí)例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • vue-router 2.0 跳轉(zhuǎn)之router.push()用法說明

    vue-router 2.0 跳轉(zhuǎn)之router.push()用法說明

    這篇文章主要介紹了vue-router 2.0 跳轉(zhuǎn)之router.push()用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08

最新評(píng)論

克东县| 南雄市| 泸水县| 灵川县| 于都县| 博湖县| 北宁市| 常熟市| 新乡市| 东莞市| 砚山县| 城固县| 苏尼特右旗| 崇阳县| 余庆县| 长阳| 普宁市| 常德市| 灵石县| 通化市| 肇庆市| 共和县| 维西| 大厂| 台中市| 怀宁县| 湖口县| 石家庄市| 南安市| 江永县| 保山市| 青州市| 武安市| 桂林市| 化德县| 电白县| 彰化市| 嫩江县| 马关县| 普陀区| 山阴县|