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

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

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

前言

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

一、 核心原理

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

二、 方案對比與實現(xiàn)

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

這是最基礎(chǔ)的方案,通過計算絕對位置來判斷。

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

代碼實現(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動時高頻觸發(fā)導(dǎo)致卡頓
window.addEventListener('scroll', throttle(lazyLoad, 200));

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

此方法通過getBoundingClientRect獲取元素相對于瀏覽器視口的位置來判斷,邏輯更簡潔。

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

代碼實現(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),且沒有超出視口底部
  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這是目前最推薦的方案,它是異步的,不會阻塞主線程,且不需要手動計算位置,性能最高。

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

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

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

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

代碼實現(xiàn):

const observerOptions = {
  root: null, // 默認(rèn)為瀏覽器視口
  rootMargin: '0px 0px 50px 0px', // 提前 50px 觸發(fā)加載,提升用戶體驗
  threshold: 0.1 // 交叉比例達(dá)到 10% 時觸發(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);
// 觀察頁面中所有帶 data-src 的圖片
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

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

三、總結(jié)

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

四、補充

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

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

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

相關(guān)文章

最新評論

屯留县| 溧阳市| 中山市| 文昌市| 常州市| 三亚市| 阿拉尔市| 新邵县| 疏附县| 光泽县| 巫山县| 陵川县| 潍坊市| 陆丰市| 姚安县| 自贡市| 五指山市| 普安县| 古丈县| 房山区| 唐山市| 游戏| 吴桥县| 玛纳斯县| 三河市| 鹤峰县| 昭觉县| 濮阳市| 休宁县| 宜君县| 宜川县| 鄂托克前旗| 周至县| 新野县| 五常市| 屏山县| 手游| 仙桃市| 永年县| 衡阳市| 定州市|