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

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

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

一、原理概述

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

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

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

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

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

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

配置項(xiàng)定義

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

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

/** 真實(shí)圖片地址模板 - 接收索引參數(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 會(huì)自動(dòng)把所有 DOM 存入一個(gè)數(shù)組里
 * ref<HTMLImageElement[]> 表示引用數(shù)組類型
 */
const imgRefs = ref<HTMLImageElement[]>([])
</script>

懶加載核心邏輯

/** IntersectionObserver 實(shí)例引用,組件銷毀時(shí)需要手動(dòng)清理 */
let observer: IntersectionObserver | null = null

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

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

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

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

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

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

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

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

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

  // 遍歷所有圖片,先取消對(duì)每個(gè)圖片的觀察
  // 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()
})

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

三、完整代碼示例

<template>
  <div class="app-content">
    <!-- 功能說明區(qū)域:突出懶加載的核心優(yōu)勢(shì) -->
    <div class="lazy-desc">?? 圖片懶加載功能 | 核心優(yōu)勢(shì):進(jìn)入視口才加載圖片 → 首屏加載速度提升 80%、節(jié)省帶寬資源、避免頁面卡頓,大幅優(yōu)化多圖場(chǎng)景用戶體驗(yàn)</div>
    <!-- 圖片列表容器,使用 grid 布局實(shí)現(xiàn)響應(yīng)式排版 -->
    <div class="card-list">
      <!-- v-for 循環(huán)生成 99 張圖片 -->
      <!-- ref="imgRefs" 會(huì)將每個(gè)圖片 DOM 存入 imgRefs 數(shù)組 -->
      <!-- :src 初始為占位圖,:data-src 存儲(chǔ)真實(shí)圖片地址 -->
      <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í)圖片地址生成函數(shù) - 接收索引,返回唯一隨機(jī)圖片 URL */
const IMG_URL_TEMPLATE = (index: number) => `https://picsum.photos/400/600?r=${index}`
/**
 * DOM 引用數(shù)組 - 用于存儲(chǔ)所有需要懶加載的圖片 DOM
 * Vue 會(huì)自動(dòng)將 v-for 中的 ref 收集到這個(gè)數(shù)組
 */
const imgRefs = ref<HTMLImageElement[]>([])
/** 觀察者實(shí)例 - 全局保存,組件銷毀時(shí)需要手動(dòng)清理 */
let observer: IntersectionObserver | null = null
/**
 * 初始化懶加載核心邏輯
 * 1. 創(chuàng)建 IntersectionObserver 實(shí)例
 * 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)入視口」的元素,「離開視口」時(shí)跳過
        if (!entry.isIntersecting) continue
        // 獲取觸發(fā)回調(diào)的圖片 DOM 元素
        const img = entry.target as HTMLImageElement
        // 從 data-src 屬性讀取真實(shí)圖片地址
        const realSrc = img.dataset.src
        // 將真實(shí)地址賦值給 src,觸發(fā)圖片加載
        if (realSrc) img.src = realSrc
        // ?? 關(guān)鍵:加載完成后立即取消觀察
        // 避免已加載圖片繼續(xù)占用觀察者資源
        observer.unobserve(img)
      }
    },
    {
      // threshold: 觸發(fā)加載的可見比例
      // 0.01 = 圖片露出 1% 時(shí)就觸發(fā),適合需要提前加載的場(chǎng)景
      threshold: 0.01,
    },
  )
  // 等待 Vue 更新 DOM 后再執(zhí)行監(jiān)聽
  // 確保 v-for 循環(huán)的 img 元素已經(jīng)渲染到頁面
  await nextTick()
  // 將所有圖片 DOM 注冊(cè)到觀察者,開始監(jiān)聽
  imgRefs.value.forEach((img) => observer?.observe(img))
}
/**
 * 銷毀觀察者,釋放資源
 * ?? 必須在組件銷毀時(shí)調(diào)用,防止內(nèi)存泄漏
 */
function destroyLazyLoad() {
  if (!observer) return
  // 先取消所有圖片的觀察
  imgRefs.value.forEach((img) => observer!.unobserve(img))
  // 完全銷毀觀察者實(shí)例
  observer.disconnect()
  // 重置為 null
  observer = null
}
/** 組件掛載時(shí)啟動(dòng)懶加載 */
onMounted(() => {
  initLazyLoad()
})
/** 組件銷毀前清理資源 */
onUnmounted(() => {
  destroyLazyLoad()
})
</script>
<style lang="scss" scoped>
.app-content {
  /* CSS 變量:統(tǒng)一樣式配置,方便維護(hù) */
  --item-gap: 16px; /* 網(wǎng)格項(xiàng)之間的間距 */
  --item-min-width: 150px; /* 網(wǎng)格項(xià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)格布局 - 自動(dò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; /* 隱藏圖片放大時(shí)超出邊框的部分 */
}
.card-list .item:hover img {
  transform: scale(1.5); /* 鼠標(biāo)懸停時(shí)圖片放大 1.5 倍 */
}
.card-list .item img {
  display: block;
  width: 100%;
  height: 100%;
  transition: all 0.32s; /* 過渡動(dòng)畫,使縮放更平滑 */
}
</style>

四、核心總結(jié)

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

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

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

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

相關(guān)文章

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

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

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

    泉州市| 南宫市| 屏东县| 微博| 五指山市| 绵竹市| 内黄县| 九龙城区| 互助| 沈丘县| 香河县| 井冈山市| 外汇| 南陵县| 威信县| 太谷县| 泰安市| 古交市| 南雄市| 恭城| 虹口区| 丘北县| 花莲县| 吴江市| 汨罗市| 亚东县| 湾仔区| 大同市| 保德县| 元氏县| 惠来县| 通化县| 利津县| 米易县| 北辰区| 高碑店市| 息烽县| 新疆| 沙洋县| 山阴县| 金坛市|