溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

發(fā)布時(shí)間:2021-08-25 10:49:29 來源:億速云 閱讀:110 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹了前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

    一.何為懶加載?:

    ??在我們訪問一個(gè)圖片展示比較多的網(wǎng)頁時(shí),加載速度慢很多時(shí)候正是因?yàn)閳D片多導(dǎo)致,大量的img圖片導(dǎo)致頁面渲染的堵塞。當(dāng)費(fèi)了許多力氣把全部圖片和頁面加載出來時(shí)而用戶早已離去。另一方面,若用戶只查看了網(wǎng)頁的前面部分便離開,許多已經(jīng)加載卻因?yàn)樘幱诰W(wǎng)頁底部而未呈現(xiàn)在視口區(qū)的圖片,它們極大加重服務(wù)器壓力了但是用戶看都沒看,白白浪費(fèi)了性能。

    ??為了解決上面的問題需要引入圖片懶加載,懶加載其實(shí)很好理解,重點(diǎn)就是一個(gè)‘懶'字。當(dāng)用戶滾動(dòng)相應(yīng)可視區(qū)域,若可視區(qū)域有圖片便加載,而在可視區(qū)域外未加載過的圖片它們先不加載,如果用戶滾動(dòng)可視區(qū)域到它們時(shí)它們再加載,否則一律不加載。這樣一來就大大提高了網(wǎng)頁渲染的性能和減少不必要的浪費(fèi)。

    二.實(shí)現(xiàn)懶加載?:

    ??首先,先定義一個(gè)基本的HTML頁面模擬一些存在大量圖片的網(wǎng)頁,比如我用8個(gè)img 標(biāo)簽來模擬,同時(shí)定義一些基本css樣式,代碼與初始效果如下:

    html:

    <img src="img/1.jpg" alt="xxx" />
        <img src="img/2.jpg" alt="xxx" />
        <img src="img/3.jpg" alt="xxx" />
        <img src="img/4.jpg" alt="xxx" />
        <img src="img/5.jpg" alt="xxx" />
        <img src="img/6.jpg" alt="xxx" />
        <img src="img/7.jpg" alt="xxx" />
        <img src="img/8.jpg" alt="xxx" />

    css:

     * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
          }
          img {
            width: 500px;
            height: 300px;
            object-fit: cover;
            margin: 20px;
          }
          body {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-evenly;
          }

    初始效果如下,可以看到右邊的控制臺,8張圖片在我一運(yùn)行這個(gè)頁面的時(shí)候就都一同被加載渲染了:

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載??

    下面是利用JavaScript實(shí)現(xiàn)懶加載的3種方式,原理都是判斷圖片是否出現(xiàn)在可視區(qū)后給圖片賦值src屬性。

    2.1 第一種方式:

    ??首先,修改每一個(gè)img標(biāo)簽,利用HTML提供的 data- 屬性來嵌入自定義數(shù)據(jù),這個(gè)自定義數(shù)據(jù)我們存放這個(gè)標(biāo)簽原本的圖片地址。同時(shí),全部的圖片的src屬性我們都用一張同樣的圖表示,這個(gè)圖一般可為顯示載入中字樣的圖片。注意,如果多個(gè)img標(biāo)簽src引用的是同一張圖片,那么只會加載一次,不會多次加載,所以我下面給每個(gè)圖src定義同一張圖。

    <img data-src="img/1.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/2.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/3.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/4.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/5.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/6.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/7.jpg" src="img/0.png" alt="xxx" />
        <img data-src="img/8.jpg" src="img/0.png" alt="xxx" />

    此時(shí)頁面效果如下,:

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

    接下來用JavaScript實(shí)現(xiàn)當(dāng)我們滾動(dòng)滾動(dòng)條時(shí),如果圖片出現(xiàn)在可視區(qū),那么加載圖片。加載圖片其實(shí)就是給img標(biāo)簽src屬性賦值為本來的地址,那么此時(shí)圖片便會請求加載渲染出來。

     //獲取全部img標(biāo)簽
    var images = document.getElementsByTagName("img");
    
     window.addEventListener("scroll", (e) => {
        //當(dāng)發(fā)生滾動(dòng)事件時(shí)調(diào)用ergodic事件
        ergodic();
      });
      function ergodic() {
        // 遍歷每一張圖
        for (let i of images) {
          //判斷當(dāng)前圖片是否在可視區(qū)內(nèi)
          if (i.offsetTop <= window.innerHeight + window.scrollY) {
              //獲取自定義data-src屬性的值
              let trueSrc = i.getAttribute("data-src");
              //把值賦值給圖片的src屬性
              i.setAttribute("src", trueSrc);
          }
        }
      }
      //沒發(fā)生滾動(dòng)事件時(shí)也要先執(zhí)行一次
      ergodic();

    ?其中, offsetTop 為元素距離頂部的距離;window.innerHeight 為當(dāng)前窗口的高度;window.scrollY 為滾動(dòng)距離;不難知道,當(dāng) i.offsetTop <= window.innerHeight + window.scrollY時(shí)圖片就處于窗口可視區(qū)了。

    此時(shí)效果如下,觀察右側(cè)控制臺,發(fā)現(xiàn)當(dāng)滾動(dòng)時(shí)圖片才加載:

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

    2.2 第二種方式:

    第二種方式其實(shí)和第一種差不多,只是計(jì)算圖片是否在可視區(qū)方式不同,重復(fù)的部分就省略了,如下:

      window.addEventListener("scroll", (e) => {
            ergodic();
          });
          function ergodic() {
            for (let i of images) {
              //計(jì)算方式和第一種方式不同
              if (i.getBoundingClientRect().top < window.innerHeight) {
                let trueSrc = i.getAttribute("data-src");
                i.setAttribute("src", trueSrc);
              }
            }
          }
          ergodic();

    ?其中,getBoundingClientRect().top 為元素相對于窗口的位置;window.innerHeight 為當(dāng)前窗口的高度;當(dāng)元素對于窗口的位置小于當(dāng)前窗口的高度時(shí),那自然處于了窗口可視區(qū)了。

    效果一樣的:

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

    2.3 第三種方式(優(yōu)):

    ?其實(shí)上面兩種方式已經(jīng)大致實(shí)現(xiàn)懶加載,但是,它們都有一個(gè)缺點(diǎn),就是一當(dāng)發(fā)生滾動(dòng)事件時(shí),就發(fā)生了大量的循環(huán)和判斷操作判斷圖片是否可視區(qū)里。這自然是不太好的,那是否有解決方法。這里就引入了一個(gè)叫 Intersection Observer 觀察器接口,它是是瀏覽器原生提供的構(gòu)造函數(shù),使用它能省到大量的循環(huán)和判斷。當(dāng)然它的兼容可能不太好,看情況使用。

    ?Intersection Observer 是什么呢?這個(gè)構(gòu)造函數(shù)的作用是它能夠觀察可視窗口與目標(biāo)元素產(chǎn)生的交叉區(qū)域。簡單來說就是當(dāng)用它觀察我們的圖片時(shí),當(dāng)圖片出現(xiàn)或者消失在可視窗口,它都能知道并且會執(zhí)行一個(gè)特殊的回調(diào)函數(shù),我們就利用這個(gè)回調(diào)函數(shù)實(shí)現(xiàn)我們的操作。概念枯燥難懂,直接看下面例子:

    1.既然IntersectionObserver是瀏覽器原生提供的構(gòu)造函數(shù),先new一個(gè)實(shí)例:

     const observer = new IntersectionObserver(callback);

    ?其中它會有一個(gè)參數(shù)callback,參數(shù)為一個(gè)回調(diào)函數(shù),當(dāng)目標(biāo)元素能看見會觸發(fā)一次,目標(biāo)元素看不見會再觸發(fā)一次。

    2.使用實(shí)例通過observer屬性可以為每一張圖片綁定一個(gè)觀察器:

    for (let i of images) {
            observer.observe(i);
          }

    3.由上可以知道每張圖片能看見會和看不見時(shí)都會觸發(fā)一次callback回調(diào)函數(shù),同時(shí)callback這個(gè)回調(diào)函數(shù)也有一個(gè)參數(shù)entries,我們可以運(yùn)行觸發(fā)這個(gè)回調(diào)函數(shù)看看它是什么:

     function callback(entries) {
          console.log(entries)
          }

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

    ?可以看到每次圖片能看見會和看不見時(shí)都會觸發(fā)一次callback回調(diào)函數(shù),并輸出了參數(shù)entries的內(nèi)容。其實(shí),entries為一個(gè)數(shù)組,而它的數(shù)組元素為當(dāng)前改變了狀態(tài)觸發(fā)了事件的目標(biāo)元素。其中有一個(gè)isIntersecting屬性,當(dāng)目標(biāo)元素在視口看得見為它true,不在時(shí)它為 false 。我們就可以利用這個(gè)屬性,當(dāng)它為 true 時(shí)設(shè)置觸發(fā)這個(gè)事件的圖片的src屬性值為data-src,開始加載。

    function callback(entries) {
            for (let i of entries) {
              if (i.isIntersecting) {
                  let img = i.target;
                  let trueSrc = img.getAttribute("data-src");
                  img.setAttribute("src", trueSrc);
              }
            } 
          }

    ?其中 target 事件屬性返回觸發(fā)事件的元素。當(dāng)前,當(dāng)回來滾動(dòng)時(shí),圖片會一會可見一會不可見,它都是觸發(fā)回調(diào)函數(shù),所以當(dāng)某圖片已經(jīng)加載時(shí)我們要停掉它的觀察器。利用unobserve屬性可停掉。

      function callback(entries) {
        for (let i of entries) {
          if (i.isIntersecting) {
              let img = i.target;
              let trueSrc = img.getAttribute("data-src");
              img.setAttribute("src", trueSrc);
              // 結(jié)束觀察
              observer.unobserve(img);
          }
        } 
      }

    完整代碼:

     var images = document.getElementsByTagName("img");
       function callback(entries) {
        for (let i of entries) {
          if (i.isIntersecting) {
              let img = i.target;
              let trueSrc = img.getAttribute("data-src");
              img.setAttribute("src", trueSrc);
              observer.unobserve(img);
          }
        } 
      }
          const observer = new IntersectionObserver(callback);
          for (let i of images) {
            observer.observe(i);
          }

    效果如下,實(shí)現(xiàn)懶加載:

    前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載

    感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“前端開發(fā)中如何實(shí)現(xiàn)圖片懶加載”這篇文章對大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI