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

前端實(shí)現(xiàn)圖片懶加載的三種手寫方案詳解

 更新時(shí)間:2026年02月10日 09:01:49   作者:發(fā)現(xiàn)一只大呆瓜  
如果一次性加載所有圖片會(huì)導(dǎo)致首屏白屏?xí)r間FP過(guò)長(zhǎng),消耗用戶大量流量,這時(shí)就需要圖片懶加載,本文為大家整理了前端實(shí)現(xiàn)圖片懶加載的三種手寫方案,大家可以根據(jù)需要進(jìn)行選擇

前言

在電商、社交等圖片密集型應(yīng)用中,一次性加載所有圖片會(huì)導(dǎo)致首屏白屏?xí)r間(FP)過(guò)長(zhǎng),消耗用戶大量流量。圖片懶加載的核心思想就是: “按需加載” ——只有當(dāng)圖片進(jìn)入或即將進(jìn)入可視區(qū)域時(shí),才真正發(fā)起網(wǎng)絡(luò)請(qǐng)求。

一、 核心原理

  • 占位圖:初始化時(shí),圖片的 src 屬性指向一張極小的 base64 圖片或 loading 占位圖。
  • 存儲(chǔ)地址:將真實(shí)的圖片 URL 存放在自定義屬性中(如 data-src)。
  • 觸發(fā)判斷:通過(guò) JS 監(jiān)聽位置變化,當(dāng)圖片進(jìn)入可視區(qū),將 data-src 的值賦給 src。

二、 方案對(duì)比與實(shí)現(xiàn)

方案 1:傳統(tǒng)滾動(dòng)監(jiān)聽(Scroll + OffsetTop)

這是最基礎(chǔ)的方案,通過(guò)計(jì)算絕對(duì)位置來(lái)判斷。

公式: window.innerHeight (可視窗口高) + document.documentElement.scrollTop (滾動(dòng)條高度) > element.offsetTop (元素距離頁(yè)面頂部高度)

代碼實(shí)現(xiàn):

function lazyLoad() {
  const images = document.querySelectorAll('img[data-src]');
  const clientHeight = window.innerHeight;
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

  images.forEach(img => {
    // 判斷是否進(jìn)入可視區(qū)
    if (clientHeight + scrollTop > img.offsetTop) {
      img.src = img.dataset.src;
      img.removeAttribute('data-src'); // 加載后移除屬性,防止重復(fù)執(zhí)行
    }
  });
}

// 注意:必須添加節(jié)流(Throttle),防止?jié)L動(dòng)時(shí)高頻觸發(fā)導(dǎo)致卡頓
window.addEventListener('scroll', throttle(lazyLoad, 200));

方案 2:現(xiàn)代位置屬性(getBoundingClientRect)

此方法通過(guò)getBoundingClientRect獲取元素相對(duì)于瀏覽器視口的位置來(lái)判斷,邏輯更簡(jiǎn)潔。

判斷條件: rect.top(元素頂部距離視口頂部的距離) < window.innerHeight。

代碼實(shí)現(xiàn):

function handleScroll() {
  const img = document.getElementById('target-img');
  const rect = img.getBoundingClientRect();
  const viewHeight = window.innerHeight || document.documentElement.clientHeight;

  // 元素頂部出現(xiàn)在視口內(nèi),且沒(méi)有超出視口底部
  if (rect.top >= 0 && rect.top < viewHeight) {
    console.log('圖片進(jìn)入可視區(qū),開始加載');
    img.src = img.dataset.src;
    window.removeEventListener('scroll', handleScroll); // 加載后卸載監(jiān)聽
  }
}

window.addEventListener('scroll', handleScroll);

方案 3:最優(yōu)解方案(IntersectionObserver API)

IntersectionObserver這是目前最推薦的方案,它是異步的,不會(huì)阻塞主線程,且不需要手動(dòng)計(jì)算位置,性能最高。

使用語(yǔ)法:const observer = new IntersectionObserver(callback, options)

callback:是元素可見性發(fā)生變化時(shí)的回調(diào)函數(shù),接收兩個(gè)參數(shù):

entries:觀察目標(biāo)的對(duì)象數(shù)組。對(duì)象中存在isIntersecting屬性(布爾值),代表目標(biāo)元素是否與根元素交叉(即進(jìn)入視口)。

options:配置對(duì)象(該參數(shù)可選)。其中 root表示指定一個(gè)根元素,默認(rèn)是瀏覽器窗口、rootMargin表示控制根元素的外邊距、threshold 為目標(biāo)元素與根元素中的可見比例,可以通過(guò)設(shè)置值來(lái)觸發(fā)回調(diào)函數(shù)

代碼實(shí)現(xiàn):

const observerOptions = {
  root: null, // 默認(rèn)為瀏覽器視口
  rootMargin: '0px 0px 50px 0px', // 提前 50px 觸發(fā)加載,提升用戶體驗(yàn)
  threshold: 0.1 // 交叉比例達(dá)到 10% 時(shí)觸發(fā)
};

const handleIntersection = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      console.log('IntersectionObserver 推送:圖片加載成功');
      observer.unobserve(img); // 停止觀察該元素
    }
  });
};

const observer = new IntersectionObserver(handleIntersection, observerOptions);
// 觀察頁(yè)面中所有帶 data-src 的圖片
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

方案 1 和 2 在獲取 offsetTopgetBoundingClientRect 時(shí)會(huì)強(qiáng)制瀏覽器重新計(jì)算布局(回流),在長(zhǎng)列表場(chǎng)景下可能導(dǎo)致掉幀。方案 3 不受此影響

三、總結(jié)

特性方案 1 (OffsetTop)方案 2 (Rect)方案 3 (Intersection)
計(jì)算復(fù)雜度較高 (需計(jì)算累計(jì)高度)中等極低 (引擎原生實(shí)現(xiàn))
性能消耗高 (頻繁觸發(fā)回流)高 (觸發(fā)回流)低 (異步非阻塞)
兼容性極好 (所有瀏覽器)好 (IE9+)一般 (現(xiàn)代瀏覽器, IE 需 Polyfill)

四、補(bǔ)充

現(xiàn)代瀏覽器(Chrome 76+)已原生支持 <img loading="lazy">,如果是簡(jiǎn)單場(chǎng)景,一行 HTML 屬性即可搞定。

務(wù)必給圖片設(shè)置固定的寬高比或底色占位。否則圖片加載前高度為 0,加載瞬間高度撐開會(huì)引發(fā)劇烈的頁(yè)面抖動(dòng)。

到此這篇關(guān)于前端實(shí)現(xiàn)圖片懶加載的三種手寫方案詳解的文章就介紹到這了,更多相關(guān)前端圖片懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

通化市| 舟山市| 吴川市| 格尔木市| 武鸣县| 平果县| 霍林郭勒市| 宁武县| 察雅县| 辽阳县| 江油市| 古交市| 连城县| 福建省| 隆德县| 周宁县| 防城港市| 新田县| 秦安县| 吕梁市| 濮阳市| 双流县| 永登县| 临猗县| 郯城县| 通城县| 西和县| 甘南县| 嘉兴市| 龙泉市| 阿拉善右旗| 荣成市| 望江县| 浙江省| 呼和浩特市| 石首市| 惠来县| 十堰市| 平和县| 辽阳县| 凤城市|