Vue自定義指令實(shí)現(xiàn)圖片懶加載

什么是圖片懶加載

進(jìn)入頁面的時(shí)候,只請(qǐng)求可視區(qū)域的圖片資源

懶加載原理

圖片的標(biāo)簽是 img 標(biāo)簽,圖片的來源主要是 src 屬性,瀏覽器是否發(fā)起加載圖片的請(qǐng)求是根據(jù)是否有src屬性決定的。
所以可以從 img 標(biāo)簽的 src 屬性入手,在沒進(jìn)到可視區(qū)域的時(shí)候,就先不給 img 標(biāo)簽的 src 屬性賦值。

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

  1. 監(jiān)聽 scroll 事件判斷元素是否進(jìn)入視口
const imgs = [...document.getElementsByTagName('img')];
let n = 0;

lazyload();

function throttle(fn, wait) {
    let timer = null;
    return function (...args) {
        if (!timer) {
            timer = setTimeout(() => {
                timer = null;
                fn.apply(this, args)
            }, wait)
        }
    }
}

function lazyload() {
    var innerHeight = window.innerHeight;
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    for (let i = n; i < imgs.length; i++) {
        if (imgs[i].offsetTop < innerHeight + scrollTop) {
            imgs[i].src = imgs[i].getAttribute("data-src");
            n = i + 1;
        }

    }
}
window.addEventListener('scroll', throttle(lazyload, 200));

存在問題:

  • 每次滑動(dòng)都要執(zhí)行一次循環(huán),如果有1000多個(gè)圖片,性能會(huì)很差
  • 每次讀取 scrollTop 都會(huì)引起回流
  • scrollTop 跟 DOM 的嵌套關(guān)系有關(guān),應(yīng)該根據(jù) getboundingclientrect 獲?。╣etBoundingClientRect 用于獲取某個(gè)元素相對(duì)于視窗的位置集合,集合中有top, right, bottom, left 等屬性)
  • 滑到最后的時(shí)候刷新,會(huì)看到所有的圖片都加載了

tips:
當(dāng) render tree 中的一部分(或全部)因?yàn)樵氐囊?guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建,稱為回流。每個(gè)頁面至少需要一次回流,就是在頁面第一次加載的時(shí)候。
當(dāng) render tree 中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會(huì)影響布局的,比如 background-color,稱為重繪。
注:回流必將引起重繪,而重繪不一定會(huì)引起回流。

  1. IntersectionObserver
    IntersectionObserver 接口提供了一種異步觀察目標(biāo)元素與其祖先元素或頂級(jí)文檔視窗(viewport)交叉狀態(tài)的方法。祖先元素與視窗(viewport,可視視口)被稱為根(root)。

    當(dāng)一個(gè) IntersectionObserver 對(duì)象被創(chuàng)建時(shí),其被配置為監(jiān)聽根中一段給定比例的可見區(qū)域。一旦 IntersectionObserver 被創(chuàng)建,則無法更改其配置,所以一個(gè)給定的觀察者對(duì)象只能用來監(jiān)聽可見區(qū)域的特定變化值;然而,你可以在同一個(gè)觀察者對(duì)象中配置監(jiān)聽多個(gè)目標(biāo)元素。

const imgs = [ ...document.getElementsByTagName('img') ];
// 當(dāng)監(jiān)聽的元素進(jìn)入可視范圍內(nèi)的會(huì)觸發(fā)回調(diào)
if (IntersectionObserver) {
    // 創(chuàng)建一個(gè) intersection observer
    const lazyImageObserver = new IntersectionObserver((entries, observer) => {
        entries.forEach((entry, index) => {
            let lazyImage = entry.target;
            // 相交率,默認(rèn)是相對(duì)于瀏覽器視窗
            if (entry.intersectionRatio > 0) {
                lazyImage.src = lazyImage.getAttribute('data-src');
                // 當(dāng)前圖片加載完之后需要去掉監(jiān)聽
                lazyImageObserver.unobserve(lazyImage);
            }

        })
    })
    for (let i = 0; i < imgs.length; i++) {
        lazyImageObserver.observe(imgs[i]);
    }
}
  1. vue自定義指令-懶加載
<img v-lazyload="image">

Vue.directive('lazyload', {
    // 指令的定義
    bind: function(el, binding) {
        let lazyImageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach((entry, index) => {
                let lazyImage = entry.target;
                // 相交率,默認(rèn)是相對(duì)于瀏覽器視窗
                if(entry.intersectionRatio > 0) {
                    lazyImage.src = binding.value;
                    // 當(dāng)前圖片加載完之后需要去掉監(jiān)聽
                    lazyImageObserver.unobserve(lazyImage);
                }

            })
        })
        lazyImageObserver.observe(el);
    },
});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容