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

基于Vue3+IntersectionObserver實現(xiàn)高性能圖片懶加載

 更新時間:2026年05月05日 08:34:46   作者:當(dāng)時只道尋常  
本文詳解 Vue3 中如何使用 IntersectionObserver API 實現(xiàn)圖片懶加載,核心優(yōu)勢在于進(jìn)入視口才加載圖片,可顯著提升首屏加載速度、節(jié)省帶寬資源、避免頁面卡頓,適合多圖列表場景,需要的朋友可以參考下

一、原理概述

圖片懶加載的核心思想是:圖片進(jìn)入用戶可視區(qū)域時才加載真實圖片,未進(jìn)入時顯示占位圖

Vue3 中實現(xiàn)懶加載最優(yōu)雅的方式是使用 IntersectionObserver API,相比傳統(tǒng)的 scroll 事件監(jiān)聽,它具備以下優(yōu)勢:

  • 性能更好:瀏覽器自動優(yōu)化交叉觀察,無需手動計算位置
  • 更省資源:元素離開視口后自動暫停監(jiān)聽
  • 代碼更簡潔:幾行配置即可完成復(fù)雜的懶加載邏輯

懶加載實現(xiàn)流程:

  1. 頁面初始時,圖片 src 使用占位圖,真實地址存在 data-src 屬性中
  2. 創(chuàng)建 IntersectionObserver 實例,監(jiān)聽所有圖片元素
  3. 當(dāng)圖片進(jìn)入視口(露出比例超過閾值)時,將 data-src 的值賦給 src
  4. 圖片加載完成后取消觀察,釋放資源

二、核心代碼實現(xiàn)

配置項定義

<script setup lang="ts">
/** 圖片總數(shù) */
const TOTAL_ITEMS = 99

/** 默認(rèn)占位圖 - 頁面初始時顯示的輕量圖片 */
const DEFAULT_IMG = 'https://pica.zhimg.com/v2-f052aa50ca65df4bad1c3b7e4084d00e_1440w.jpg'

/** 真實圖片地址模板 - 接收索引參數(shù),生成不同的隨機(jī)圖片 URL */
const IMG_URL_TEMPLATE = (index: number) => `https://picsum.photos/400/600?r=${index}`
</script>

DOM 引用獲取

<script setup lang="ts">
/**
 * 獲取所有需要懶加載的圖片 DOM 引用
 * 在 v-for 中使用 ref,Vue 會自動把所有 DOM 存入一個數(shù)組里
 * ref<HTMLImageElement[]> 表示引用數(shù)組類型
 */
const imgRefs = ref<HTMLImageElement[]>([])
</script>

懶加載核心邏輯

/** IntersectionObserver 實例引用,組件銷毀時需要手動清理 */
let observer: IntersectionObserver | null = null

/**
 * 初始化懶加載監(jiān)聽
 * 使用 async 是為了確保 DOM 渲染完成后再執(zhí)行監(jiān)聽
 */
async function initLazyLoad() {
  // 創(chuàng)建觀察者實例,傳入回調(diào)函數(shù)和配置項
  observer = new IntersectionObserver(
    // entries: 觸發(fā)回調(diào)時,傳入所有發(fā)生交叉變化的元素數(shù)組
    // observer: 觀察者實例本身,用于調(diào)用 unobserve 取消觀察
    (entries, observer) => {
      // 遍歷所有發(fā)生變化的元素
      for (const entry of entries) {
        // isIntersecting: 元素是否進(jìn)入視口
        // ! 為 false 時表示元素離開了視口,無需處理,直接跳過
        if (!entry.isIntersecting) continue

        // 將 entry.target 斷言為 HTMLImageElement 類型
        // 因為 ref 數(shù)組中存儲的正是圖片 DOM 元素
        const img = entry.target as HTMLImageElement

        // dataset: 獲取元素上 data-* 自定義屬性
        // data-src="真實圖片地址" 存儲在 dataset.src 中
        const realSrc = img.dataset.src

        // 將真實圖片地址賦值給 src,觸發(fā)瀏覽器加載真實圖片
        if (realSrc) img.src = realSrc

        // 加載完成后立即取消觀察該圖片
        // 避免已加載的圖片占用觀察者資源,提升性能
        observer.unobserve(img)
      }
    },
    {
      // threshold: 交叉比例閾值,0.01 表示圖片露出 1% 就觸發(fā)回調(diào)
      // 值范圍 0~1,值越小越早觸發(fā),但可能浪費帶寬
      threshold: 0.01,
    },
  )

  // 等待 DOM 渲染完成后再開始監(jiān)聽
  // nextTick 確保 v-for 循環(huán)的圖片 DOM 已經(jīng)渲染到頁面
  await nextTick()

  // 遍歷所有圖片 DOM,逐個注冊到觀察者中
  // observe 之后,觀察者就會開始監(jiān)聽該元素的可見性變化
  imgRefs.value.forEach((img) => observer?.observe(img))
}

資源清理(防止內(nèi)存泄漏)

/**
 * 銷毀觀察者實例
 * ?? 組件銷毀時必須調(diào)用!否則會內(nèi)存泄漏
 */
function destroyLazyLoad() {
  // 未初始化則直接返回,避免報錯
  if (!observer) return

  // 遍歷所有圖片,先取消對每個圖片的觀察
  // disconnect 之前建議先調(diào)用 unobserve,避免遺留監(jiān)聽
  imgRefs.value.forEach((img) => observer!.unobserve(img))

  // disconnect: 完全銷毀觀察者,釋放所有資源
  observer.disconnect()

  // 重置為 null,標(biāo)記已清理
  observer = null
}

生命周期鉤子綁定

/** 組件掛載到頁面后,立即初始化懶加載監(jiān)聽 */
onMounted(() => {
  initLazyLoad()
})

/**
 * 組件銷毀前,清理觀察者實例
 * 防止用戶切換頁面后,觀察者仍在后臺運行消耗資源
 */
onUnmounted(() => {
  destroyLazyLoad()
})

三、完整代碼示例

<template>
  <div class="app-content">
    <!-- 功能說明區(qū)域:突出懶加載的核心優(yōu)勢 -->
    <div class="lazy-desc">?? 圖片懶加載功能 | 核心優(yōu)勢:進(jìn)入視口才加載圖片 → 首屏加載速度提升 80%、節(jié)省帶寬資源、避免頁面卡頓,大幅優(yōu)化多圖場景用戶體驗</div>
    <!-- 圖片列表容器,使用 grid 布局實現(xiàn)響應(yīng)式排版 -->
    <div class="card-list">
      <!-- v-for 循環(huán)生成 99 張圖片 -->
      <!-- ref="imgRefs" 會將每個圖片 DOM 存入 imgRefs 數(shù)組 -->
      <!-- :src 初始為占位圖,:data-src 存儲真實圖片地址 -->
      <div class="item" v-for="(item, index) in TOTAL_ITEMS" :key="index">
        <img ref="imgRefs" :src="DEFAULT_IMG" alt="image" :data-src="IMG_URL_TEMPLATE(item)" />
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
/** 圖片總數(shù) - 控制列表中顯示的圖片數(shù)量 */
const TOTAL_ITEMS = 99
/** 默認(rèn)占位圖 - 未加載前顯示的輕量圖片 */
const DEFAULT_IMG = 'https://pica.zhimg.com/v2-f052aa50ca65df4bad1c3b7e4084d00e_1440w.jpg'
/** 真實圖片地址生成函數(shù) - 接收索引,返回唯一隨機(jī)圖片 URL */
const IMG_URL_TEMPLATE = (index: number) => `https://picsum.photos/400/600?r=${index}`
/**
 * DOM 引用數(shù)組 - 用于存儲所有需要懶加載的圖片 DOM
 * Vue 會自動將 v-for 中的 ref 收集到這個數(shù)組
 */
const imgRefs = ref<HTMLImageElement[]>([])
/** 觀察者實例 - 全局保存,組件銷毀時需要手動清理 */
let observer: IntersectionObserver | null = null
/**
 * 初始化懶加載核心邏輯
 * 1. 創(chuàng)建 IntersectionObserver 實例
 * 2. 等待 DOM 渲染完成后開始監(jiān)聽
 */
async function initLazyLoad() {
  // 創(chuàng)建觀察者,配置交叉閾值為 1%
  observer = new IntersectionObserver(
    (entries, observer) => {
      // entries: 當(dāng)前幀內(nèi)所有發(fā)生交叉變化的元素列表
      for (const entry of entries) {
        // 只處理「進(jìn)入視口」的元素,「離開視口」時跳過
        if (!entry.isIntersecting) continue
        // 獲取觸發(fā)回調(diào)的圖片 DOM 元素
        const img = entry.target as HTMLImageElement
        // 從 data-src 屬性讀取真實圖片地址
        const realSrc = img.dataset.src
        // 將真實地址賦值給 src,觸發(fā)圖片加載
        if (realSrc) img.src = realSrc
        // ?? 關(guān)鍵:加載完成后立即取消觀察
        // 避免已加載圖片繼續(xù)占用觀察者資源
        observer.unobserve(img)
      }
    },
    {
      // threshold: 觸發(fā)加載的可見比例
      // 0.01 = 圖片露出 1% 時就觸發(fā),適合需要提前加載的場景
      threshold: 0.01,
    },
  )
  // 等待 Vue 更新 DOM 后再執(zhí)行監(jiān)聽
  // 確保 v-for 循環(huán)的 img 元素已經(jīng)渲染到頁面
  await nextTick()
  // 將所有圖片 DOM 注冊到觀察者,開始監(jiān)聽
  imgRefs.value.forEach((img) => observer?.observe(img))
}
/**
 * 銷毀觀察者,釋放資源
 * ?? 必須在組件銷毀時調(diào)用,防止內(nèi)存泄漏
 */
function destroyLazyLoad() {
  if (!observer) return
  // 先取消所有圖片的觀察
  imgRefs.value.forEach((img) => observer!.unobserve(img))
  // 完全銷毀觀察者實例
  observer.disconnect()
  // 重置為 null
  observer = null
}
/** 組件掛載時啟動懶加載 */
onMounted(() => {
  initLazyLoad()
})
/** 組件銷毀前清理資源 */
onUnmounted(() => {
  destroyLazyLoad()
})
</script>
<style lang="scss" scoped>
.app-content {
  /* CSS 變量:統(tǒng)一樣式配置,方便維護(hù) */
  --item-gap: 16px; /* 網(wǎng)格項之間的間距 */
  --item-min-width: 150px; /* 網(wǎng)格項的最小寬度,響應(yīng)式適配 */
  --item-height: 300px; /* 圖片卡片固定高度 */
}
/* 功能描述樣式 - 左側(cè)藍(lán)色邊框提示框 */
.lazy-desc {
  margin-bottom: 16px;
  padding: 8px 16px;
  background: #f0f9ff; /* 淺藍(lán)色背景 */
  border-left: 4px solid #409eff; /* 左側(cè)藍(lán)色強(qiáng)調(diào)條 */
  border-radius: 4px;
  color: #1f2937;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
}
/* 響應(yīng)式網(wǎng)格布局 - 自動填充,最小寬度 150px */
.card-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--item-min-width), 1fr));
  gap: var(--item-gap);
}
.card-list .item {
  cursor: pointer;
  height: var(--item-height);
  border-radius: 4px;
  box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35); /* 卡片陰影 */
  overflow: hidden; /* 隱藏圖片放大時超出邊框的部分 */
}
.card-list .item:hover img {
  transform: scale(1.5); /* 鼠標(biāo)懸停時圖片放大 1.5 倍 */
}
.card-list .item img {
  display: block;
  width: 100%;
  height: 100%;
  transition: all 0.32s; /* 過渡動畫,使縮放更平滑 */
}
</style>

四、核心總結(jié)

本文通過 Vue3 + IntersectionObserver 實現(xiàn)了高性能圖片懶加載方案,核心要點:

要點說明
IntersectionObserver替代 scroll 事件,瀏覽器自動優(yōu)化,性能更優(yōu)
占位圖 + data-src初始顯示占位圖,真實地址存在 data-src 中
observer.unobserve()加載完成后取消監(jiān)聽,避免資源浪費
onUnmounted 清理組件銷毀時調(diào)用 disconnect(),防止內(nèi)存泄漏

該方案在多圖列表場景下效果顯著,可直接應(yīng)用于商品列表、朋友圈圖片流、相冊等業(yè)務(wù)場景。

以上就是基于Vue3+IntersectionObserver實現(xiàn)高性能圖片懶加載的詳細(xì)內(nèi)容,更多關(guān)于Vue3 IntersectionObserver圖片懶加載的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue自定義組件實現(xiàn)v-model雙向綁定數(shù)據(jù)的實例代碼

    vue自定義組件實現(xiàn)v-model雙向綁定數(shù)據(jù)的實例代碼

    vue中父子組件通信,都是單項的,直接在子組件中修改prop傳的值vue也會給出一個警告,接下來就用一個小列子一步一步實現(xiàn)了vue自定義的組件實現(xiàn)v-model雙向綁定,需要的朋友可以參考下
    2021-10-10
  • vue如何使用element-ui 實現(xiàn)自定義分頁

    vue如何使用element-ui 實現(xiàn)自定義分頁

    這篇文章主要介紹了vue如何使用element-ui 實現(xiàn)自定義分頁,可以通過插槽實現(xiàn)自定義的分頁,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-07-07
  • 詳解VUE2.X過濾器的使用方法

    詳解VUE2.X過濾器的使用方法

    本篇文章主要介紹了詳解VUE2.X過濾器的使用方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 一文搞懂Vue中watch偵聽器的用法

    一文搞懂Vue中watch偵聽器的用法

    在Vue.js中,您可以使用watch選項來創(chuàng)建偵聽器,以偵聽特定屬性的變化,偵聽器可以在屬性發(fā)生變化時執(zhí)行相關(guān)的邏輯,本文給大家詳細(xì)講講Vue中watch偵聽器的用法,需要的朋友可以參考下
    2023-11-11
  • jdk1.8+vue elementui實現(xiàn)多級菜單功能

    jdk1.8+vue elementui實現(xiàn)多級菜單功能

    這篇文章主要介紹了jdk1.8+vue elementui實現(xiàn)多級菜單功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Vue3中使用Pinia修改State的五種方式

    Vue3中使用Pinia修改State的五種方式

    這篇文章主要介紹了Vue3中使用Pinia修改State的五種方式,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2023-11-11
  • vue實現(xiàn)全選功能

    vue實現(xiàn)全選功能

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)全選功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Vite+TS+Vue開啟eslint和prettier規(guī)范及校驗方式

    Vite+TS+Vue開啟eslint和prettier規(guī)范及校驗方式

    這篇文章主要介紹了Vite+TS+Vue開啟eslint和prettier規(guī)范及校驗方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • vue父子傳值,兄弟傳值,子父傳值詳解

    vue父子傳值,兄弟傳值,子父傳值詳解

    這篇文章主要介紹了Vue傳值-三種常用傳值示例,主要介紹了父組件向子組件進(jìn)行傳值,子組件向父組件傳值和非父子組件進(jìn)行傳值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • vue+webpack實現(xiàn)異步組件加載的方法

    vue+webpack實現(xiàn)異步組件加載的方法

    下面小編就為大家分享一篇vue+webpack實現(xiàn)異步組件加載的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02

最新評論

叙永县| 土默特右旗| 周宁县| 保定市| 镇雄县| 寻乌县| 都江堰市| 涪陵区| 长子县| 定兴县| 芷江| 若羌县| 精河县| 平塘县| 乡城县| 上饶市| 锡林郭勒盟| 永修县| 延庆县| 汨罗市| 凌源市| 湄潭县| 汝阳县| 阿图什市| 吐鲁番市| 云安县| 乳源| 汉川市| 盖州市| 西安市| 永福县| 河西区| 富阳市| 冀州市| 文昌市| 高陵县| 琼中| 宝应县| 昌邑市| 镇原县| 梅河口市|