Vue3實現圖片懶加載及自定義懶加載指令

Vue3實現圖片懶加載及自定義懶加載指令

  • 前言
  • 1.使用vue3-lazyload插件
  • 2.自定義v-lazy懶加載指令
    • 2.1 使用VueUse
    • 2.2 使用IntersectionObserver

前言

圖片懶加載是一種常見性能優化的方式,它只去加載可視區域圖片,而不是在網頁加載完畢后就立即加載所有圖片,能減少很多不必要的請求,極大的提升用戶體驗。

圖片懶加載的實現原理:在圖片沒進入可視區域的時候,只需要讓 img 標簽的 src 屬性指向一張默認圖片,在它進入可視區后,再替換它的 src 指向真實圖片地址即可。

本文就分享一下在vue3中實現圖片懶加載的幾種方式,包括使用插件以及自定義指令,實現的最終效果如下圖所示:
在這里插入圖片描述

1.使用vue3-lazyload插件

第一種方式就是使用插件,使用插件的方式非常簡單,只需要簡單的幾步即可實現。

Vue2中可以使用vue-lazyload插件來實現圖片懶加載,在Vue3中可以使用vue3-lazyload插件實現圖片懶加載。

  • 1.安裝vue3-lazyload插件
$ npm i vue3-lazyload
# or
$ yarn add vue3-lazyload
# or
$ pnpm i vue3-lazyload
  • 2.main.js入口文件注冊插件
import { createApp } from "vue";
import App from "./App.vue";
//引入圖片懶加載插件
import Lazyload from "vue3-lazyload";const app = createApp(App);//注冊插件
app.use(Lazyload, {loading: "@/assets/images/default.png",//可以指定加載中的圖像error: "@/assets/images/err.png",//可以指定加載失敗的圖像
});app.mount("#app");
  • 3.模板中使用v-lazy指令來延遲加載圖像
<template><ul class="container"><li v-for="item in imgList" :key="item.id"><img v-lazy="item.url" class="item" /></li></ul>
</template><script lang="ts" setup>
import { reactive } from "vue";
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((i) => {return {id: `${i}`,url: `@/assets/images/${i}.jpg`,};
});
const imgList = reactive(data);
</script><style scoped lang="scss">
.container {width: 100vw;height: 100vh;overflow: auto;.item {width: 100%;height: 200px;}
}
</style>

2.自定義v-lazy懶加載指令

  • 下面一種方式是自定義一個懶加載的指令,如何實現呢?

圖片懶加載的核心是監聽圖片是否進入可視區域,如果進入就替換src,即懶加載指令的核心。

網上看了很多教程,大多都使用Element.getBoundingClientRect()這個方法,該方法返回一個 DOMRect 對象,提供了元素的大小及其相對于視口的位置,然后監聽滾動條事件,通過img.getBoundingClientRect()進行一系列的比較來判斷圖片是否在視口內,這種方式略顯復雜。其實,只要我們能夠簡化判斷圖片是否進入可視區域這一流程,實現一個自定義的懶加載指令就很簡單了。

  • 有沒有什么簡化的方式呢?

可以通過VueUse中的useIntersectionObserver原生的IntersectionObserver api來簡化判斷圖片是否進入可視區域,下面就分別通過這兩種簡化的方式來實現一個自定義的懶加載指令。

2.1 使用VueUse

VueUse 是什么?

一款基于Vue組合式API的函數工具集。

以上是官方網站關于它的定義。

簡單的說就是一個工具函數包,它可以幫助你快速實現一些常見的功能。比如下面的一些:

  • useLocalStorage:提供在本地存儲中保存和獲取數據的功能。
  • useMouse:提供跟蹤鼠標位置和鼠標按下狀態的功能。
  • useDebounce:提供防抖功能。
  • useThrottle:提供節流功能。
  • useIntersectionObserver:提供對元素是否可見進行觀察的功能,可用于實現懶加載等效果。

本文要用到的就是其中的useIntersectionObserver這個函數,來監聽圖片的可見性。

  • 首先安裝 VueUse
npm i @vueuse/core
  • main.js入口文件導入
import { createApp } from "vue";
import App from "./App.vue";//從@vueuse/core中導入useIntersectionObserver函數
import { useIntersectionObserver } from "@vueuse/core";const app = createApp(App);app.mount("#app");
  • directive注冊v-lazy全局指令
//main.js
//注冊v-lazy全局指令,使v-lazy在所有組件中都可用
app.directive("lazy", {//節點掛載完成后調用mounted(el, binding) {useIntersectionObserver(el, ([{ isIntersecting }]) => {//判斷當前監聽元素是否進入視口區域if (isIntersecting) {el.src = binding.value;}});},
});

一個指令定義對象可以提供多個鉤子函數,比如 mounted、updated、unmounted 等,我們使用mounted,也就是在節點掛載完成后調用。指令的鉤子有兩個主要的參數:el和binding。el是指令綁定到的元素,binding中使用最多的是value,即傳遞給指令的值,例如在 v-lazy=“imgSrc” 中,值是 imgSrc對應的真實圖片地址。

然后使用useIntersectionObserver函數,它的兩個參數,一個是需要監聽的元素,另一個是回調函數,參數值isIntersecting為一個布爾值,用來判斷當前監聽元素是否進入視口區域,如果進入視口區域,那么我們就可以將圖片的真實url賦值給圖片的src。

其實上述代碼還有不完善的地方,首先是重復監聽的問題,可以進行console調試一下:

useIntersectionObserver(el, ([{ isIntersecting }]) => {console.log(isIntersecting);//測試if (isIntersecting) {el.src = binding.value;}
});

此時的效果如下圖所示:
在這里插入圖片描述
從上圖可以看到,往上滾動,監聽過的圖片會重復監聽,這是我們不想要的,會造成性能浪費。

解決思路:在監聽的圖片第一次完成加載后就停止監聽。可以利用useIntersectionObserver函數提供的stop方法,修改后的代碼如下:

app.directive("lazy", {mounted(el, binding) {const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {console.log(isIntersecting);if (isIntersecting) {el.src = binding.value;//在監聽的圖片第一次完成加載后就停止監聽stop();}});},
});

完善后的效果如下,解決了重復監聽問題。
在這里插入圖片描述
我們還可以設置一個默認圖片,當圖片還沒加載完成時,就顯示默認圖片。

app.directive("lazy", {mounted(el, binding) {el.src = "@/assets/images/default.png"; // 使用默認圖片const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {if (isIntersecting) {el.src = binding.value;//在監聽的圖片第一次完成加載后就停止監聽stop();}});},
});

此時還存在著的一個問題是,當前注冊了一個全局的自定義指令,所有的代碼邏輯全寫在入口文件中,這樣會造成代碼的臃腫。

解決思路:拆分代碼,通過插件的方法把懶加載指令封裝為插件,main.js入口文件只需負責注冊插件即可。

src下新建directive/index.js文件,專門存放自定義的插件,把代碼邏輯進行轉移。

// src/directive/index.js
import { useIntersectionObserver } from "@vueuse/core";
// 封裝插件
export const lazyPlugin = {install(app) {app.directive("lazy", {mounted(el, binding) {el.src = "@/assets/images/default.png"; const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {if (isIntersecting) {el.src = binding.value;stop();}});},});},
};

然后在main.js中注冊插件

import { createApp } from "vue";
import App from "./App.vue";
import { lazyPlugin } from "./directive";const app = createApp(App);
//注冊插件
app.use(lazyPlugin);
app.mount("#app");

定義插件可以參考Vue官網。通常一個 Vue3 的插件會暴露 install 函數,當 app 實例 use 該插件時,就會執行該函數。然后在 install 函數內部,通過 app.directive 去注冊一個全局指令,這樣就可以在組件中使用它們了。

現在的效果就和一開始介紹的效果一致了。
在這里插入圖片描述

2.2 使用IntersectionObserver

其實查看vue3-lazy源碼和useIntersectionObserver源碼,會發現,它們使用的就是原生IntersectionObserver api。那么接下來我們也可以使用這個api來實現一個自定義的懶加載指令。

  • MDN:IntersectionObserver 提供了一種異步觀察目標元素與其祖先元素或頂級文檔視口交叉狀態的方法。當它被創建時,其被配置為監聽根中一段給定比例的可見區域。當其監聽到目標元素的可見部分(的比例)超過了一個或多個閾值(threshold)時,會執行指定的回調函數。

簡單來說就是IntersectionObserver可以來判斷圖片是否進入可視區

它對應的回調函數的參數 entries,是 IntersectionObserverEntry 對象數組。當觀測的元素可見比例超過指定閾值時,就會執行該回調函數(默認閾值為 0,表示目標元素剛進入根元素可見范圍時觸發回調函數),對 entries 進行遍歷,拿到每一個 entry,然后判斷 entry.isIntersecting 是否為 true,如果是則說明 entry 對象對應的 DOM 元素進入了可視區。

具體代碼如下:

// src/directive/index.js
import defaultImg from "@/assets/images/default.png";
//定義一個數組用來存儲尚未加載的圖片
let imgsList = [];//加載圖片
function loadingImg(imgDOM) {//獲得圖片的srclet imgSrc = imgsList.filter((item) => item.el === imgDOM)[0].src;//新建Image對象實例來代替當前圖片的加載,圖片加載完畢就會觸發onload事件,替換img元素的src屬性const img = new Image();img.src = imgSrc; img.onload = function () {// 當圖片加載完成之后 替換img元素的src屬性imgDOM.src = imgSrc;};//將已加載好的圖片從數組中刪除imgsList = imgsList.filter((item) => item.el !== imgDOM);
}const io = new IntersectionObserver((entries) => {entries.forEach((item) => {// isIntersecting屬性判斷目標元素當前是否可見if (item.isIntersecting) {//加載圖片,加載完后停止監聽loadingImg(item.target);io.unobserve(item.target); }});
});export const lazyPlugin = {install(app) {app.directive("lazy", {mounted(el, binding) {el.src = defaultImg; // 使用默認圖片io.observe(el); //監聽圖片imgsList.push({ el: el, src: binding.value }); //數組中加入當前圖片},beforeUnmount(el) {//某個img元素解綁時,停止監聽,從數組中刪除io.unobserve(el);imgsList = imgsList.filter((item) => item.el !== el);},});},
};

主要思路就是:定義一個數組用來存儲尚未加載的圖片,observe方法對每個圖片進行監聽,如果當前圖片在可視區域,就加載圖片,并且從數組中刪除圖片,然后unobserve停止監聽。

最后依然需要在main.js中注冊插件,即可使用v-lazy自定義指令。

參考資料:
https://www.npmjs.com/package/vue3-lazyload
https://cn.vuejs.org/guide/reusability/custom-directives.html
https://cn.vuejs.org/guide/reusability/plugins.html
https://www.vueusejs.com/core/useIntersectionObserver/
https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

好了,以上就是本文的全部內容,如有問題,歡迎指出!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39591.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39591.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39591.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

clickhouse入門

clickhouse 1 課程介紹 和hadoop無關&#xff0c;俄羅斯&#xff0c;速度快3 介紹&特點 1 列式存儲 在線分析處理。 使用sql進行查詢。列式存儲更適合查詢分析的場景。新增時候有一個尋址的過程。更容易進行壓縮行式存儲。增刪改查都需要的時候。2 DBMS功能 包括ddl,d…

集成DTM實現跨語言分布式事務V1.0

集成DTM實現跨語言分布式事務V1.0 簡介 DTM是一款開源的分布式事務管理器&#xff0c;解決跨數據庫、跨服務、跨語言棧更新數據的一致性問題。 通俗一點說&#xff0c;DTM提供跨服務事務能力&#xff0c;一組服務要么全部成功&#xff0c;要么全部回滾&#xff0c;避免只更新…

MIMO-NOMA系統MATLAB仿真實現

非正交多址接入&#xff08;NOMA&#xff09;技術可以打破傳統的正交多址一個基本資源塊由單用戶獨占的限制&#xff0c;通過在時域和頻域的基礎上開辟新的功率域維度&#xff0c;在相同的時頻資源上通過功率復用技術允許同一個時頻資源塊由多個用戶共享&#xff0c;有效提升了…

ViewPager2與TabLayout的簡單使用

ViewPager2與TabLayout的簡單使用 MainActivity.java public class MainActivity extends AppCompatActivity {private ViewPager2 mViewPager;private TabLayout mTabLayout;private int[] icons new int[]{R.drawable.icon1, R.drawable.icon2, R.drawable.icon3, R.drawa…

如何進行無線網絡滲透測試?

今天我們將繼續深入探討Kali Linux的應用&#xff0c;這次我們將重點介紹如何使用Kali Linux進行無線網絡滲透測試。無線網絡滲透測試是評估無線網絡安全性的重要步驟&#xff0c;而Kali Linux作為一款專業的滲透測試發行版&#xff0c;提供了豐富的工具來幫助你進行這項任務。…

c++中const修飾成員函數的問題

問題引入&#xff1a; 看下面這一段代碼&#xff1a; class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year <&…

面試熱題(全排列)

給定一個不含重復數字的整數數組 nums &#xff0c;返回其 所有可能的全排列 。可以 按任意順序 返回答案。 輸入&#xff1a;nums [1,2,3] 輸出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 先在這里說明一下排列和組合的區別? 組合&#xff1a;是指從一…

前端三劍客

三劍客 萬維網聯盟&#xff08; World Wide Web Consortium &#xff09;&#xff0c;創建于1994年10月&#xff0c;主要工作是對 web 進行標準化。 ? 該組織定義了網頁的開發需要如下3門技術&#xff1a; ? - HTML:定義網頁的結構 - CSS: 定義網頁的表現&#xff0c;樣式 -…

開源數據庫Mysql_DBA運維實戰 (名詞解釋)

SQL&#xff08;Structured Query Language 即結構化查詢語言&#xff09; SQL語言主要用于存取數據、查詢數據、更新數據和管理關系數據庫系統&#xff0c;SQL語言由IBM開發。 SQL語言分類&#xff1a; DDL語句 數據庫定義語言&#xff1a;數據庫、表、視圖、索引、存儲過程…

Steam 靈感的游戲卡懸停效果

先看效果&#xff1a; 再看代碼&#xff08;查看更多&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Steam 靈感的游戲卡懸停效果</title><style>* {margin: …

構建高效外賣系統平臺:從需求到實現

隨著科技的不斷進步和人們生活節奏的加快&#xff0c;外賣成為了越來越多人的飲食選擇。為了滿足這一需求&#xff0c;開發一套高效的外賣系統平臺變得尤為重要。本文將從需求分析開始&#xff0c;逐步引導您了解如何開發一套完整的外賣系統平臺。 第一步&#xff1a;需求分析…

分類預測 | MATLAB實現EVO-CNN多輸入分類預測

分類預測 | MATLAB實現EVO-CNN多輸入分類預測 目錄 分類預測 | MATLAB實現EVO-CNN多輸入分類預測預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.MATLAB實現EVO-CNN多輸入分類預測 2.代碼說明&#xff1a;量谷優化卷積神經網絡的數據分類預測&#xff1a;要求于Matlab …

【hadoop】windows上hadoop環境的搭建步驟

文章目錄 前言基礎環境下載hadoop安裝包下載hadoop在windows中的依賴配置環境變量 Hadoop hdfs搭建創建hadfs數據目錄修改JAVA依賴修改配置文件初始化hdfs namenode啟動hdfs 前言 在大數據開發領域中&#xff0c;不得不說說傳統經典的hadoop基礎計算框架。一般我們都會將hadoo…

計算機視覺目標檢測性能指標

目錄 精確率&#xff08;Precision&#xff09;和召回率&#xff08;Recall&#xff09; F1分數&#xff08;F1 Score&#xff09; IoU&#xff08;Intersection over Union&#xff09; P-R曲線&#xff08;Precision-Recall Curve&#xff09;和 AP mAP&#xff08;mean…

Leetcode-每日一題【劍指 Offer 30. 包含min函數的棧】

題目 定義棧的數據結構&#xff0c;請在該類型中實現一個能夠得到棧的最小元素的 min 函數在該棧中&#xff0c;調用 min、push 及 pop 的時間復雜度都是 O(1)。 示例: MinStack minStack new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack…

【mysql】事務的四種特性的理解

&#x1f307;個人主頁&#xff1a;平凡的小蘇 &#x1f4da;學習格言&#xff1a;命運給你一個低的起點&#xff0c;是想看你精彩的翻盤&#xff0c;而不是讓你自甘墮落&#xff0c;腳下的路雖然難走&#xff0c;但我還能走&#xff0c;比起向陽而生&#xff0c;我更想嘗試逆風…

TOMCAT基礎

tomcat是一個基于Java開發的&#xff0c;開放源代碼的web應用服務器。它可以解析html頁面中的java代碼&#xff0c;執行動態請求&#xff0c;實現動態頁面。核心功能是將收到的http請求處理并轉發給適當的servlet來處理&#xff0c;然后將響應返回給客戶端。 優點 1&#xff0c…

Django實現音樂網站 ⑼

使用Python Django框架制作一個音樂網站&#xff0c; 本篇主要是后臺對專輯、首頁輪播圖原有功能的基礎上進行部分功能實現和顯示優化。 目錄 專輯功能優化 新增編輯 專輯語種改為下拉選項 添加單曲優化顯示 新增單曲多選 更新歌手專輯數、專輯單曲數 獲取歌手專輯數 保…

【并發編程】自研數據同步工具的優化:創建線程池多線程異步去分頁調用其他服務接口獲取海量數據

文章目錄 場景&#xff1a;解決方案 場景&#xff1a; 前段時間在做一個數據同步工具&#xff0c;其中一個服務的任務是調用A服務的接口&#xff0c;將數據庫中指定數據請求過來&#xff0c;交給kafka去判斷哪些數據是需要新增&#xff0c;哪些數據是需要修改的。 剛開始的設…

Character Animation With Direct3D 讀書筆記

角色動畫簡介 2D動畫&#xff1a;循環播放多張圖片 3D動畫&#xff1a; 骨骼動畫、變形動畫 DirectX入門 Win32 應用程序 Application類&#xff1a;處理主程序循環&#xff0c;圖形設備的初始化 Init&#xff1a;加載資源并創建圖形設備Update&#xff1a;更新游戲世界&am…