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

前端JS圖片懶加載原理方案詳解

 更新時(shí)間:2023年02月20日 14:50:30   作者:千空  
這篇文章主要為大家介紹了前端JS圖片懶加載原理方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

懶加載經(jīng)常出現(xiàn)在前端面試中,是前端性能優(yōu)化的常用技巧。懶加載也叫延遲加載,把非關(guān)鍵資源先不加載,等用到了再加載,將加載非關(guān)鍵資源的時(shí)間推遲,而加快頁(yè)面的初始加載時(shí)間。懶加載經(jīng)常被用在圖片、視頻、音頻、JavaScript 文件等資源加載上,本文主要討論圖片的懶加載。通過(guò)本文你能收獲:

  • 圖片懶加載的原理
  • 實(shí)現(xiàn)圖片懶加載的四種方案的原理以及優(yōu)缺點(diǎn)
  • 圖片懶加載的一些優(yōu)化

原理

圖片懶加載的原理是沒(méi)有在可視區(qū)域的圖片暫時(shí)不加載圖片,等進(jìn)入可視區(qū)域后在加載圖片,這樣可以減少初始頁(yè)面加載的圖片數(shù)量而提升頁(yè)面加載速度。 圖片懶加載在提升頁(yè)面加載速度的同時(shí)也會(huì)伴隨用戶(hù)看其他未展示的圖片時(shí)會(huì)有等待時(shí)間;圖片加載顯示會(huì)伴有布局抖動(dòng)等問(wèn)題。

方案

圖片懶加載的關(guān)鍵是:判斷一個(gè)元素是否在可視區(qū)域。

方案一:img的loading屬性設(shè)為“lazy”

HTMLImageElement 的 loading 屬性為一個(gè)字符串,它的值會(huì)提示 用戶(hù)代理 告訴瀏覽器不在可視視口內(nèi)的圖片該如何加載。這樣一來(lái),通過(guò)推遲圖片加載僅讓其在需要的時(shí)候加載而非頁(yè)面初始載入時(shí)立刻加載,優(yōu)化了頁(yè)面的載入。

lazy 告訴用戶(hù)代理推遲圖片加載直到瀏覽器認(rèn)為其需要立即加載時(shí)才去加載。例如,如果用戶(hù)正在往下滾動(dòng)頁(yè)面,值為 lazy 會(huì)導(dǎo)致圖片僅在馬上要出現(xiàn)在 可視視口中時(shí)開(kāi)始加載。

使用方法

<img src="xxx.jpg" loading="lazy" />  

優(yōu)點(diǎn)

只設(shè)置一個(gè)屬性不用 JavaScript 控制代碼是最簡(jiǎn)單方便的方案,性能也是比較好的。

兼容性

大部分主流瀏覽器都兼容該屬性。

缺點(diǎn)

雖然整個(gè)方案簡(jiǎn)單性能好,但問(wèn)題也是最多的,所以很少使用這種方案。

  • 前面提到圖片懶加載用戶(hù)看其他未展示的圖片時(shí)會(huì)有等待時(shí)間,一般會(huì)設(shè)置一個(gè)默認(rèn)圖片,這種方案不能設(shè)置默認(rèn)圖片
  • 圖片的加載數(shù)量和圖片的布局、可視區(qū)域尺寸有關(guān),難以控制
  • 圖片的加載順序也難以控制

該方案能粗略的實(shí)現(xiàn)圖片懶加載基本功能。

方案二:通過(guò)offsetTop來(lái)計(jì)算是否在可視區(qū)域內(nèi)

可視區(qū)域高度是 document.documentElement.clientHeight ,而可視區(qū)域的位置是在滾動(dòng)條滾動(dòng)位置 scrollTopscrollTop+document.documentElement.clientHeight之間。因此通過(guò) image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop 判斷圖片是否可以在可視區(qū)域內(nèi)。

  function lazyload() {
        var lazyImages = document.querySelectorAll(".lazyload");
        lazyImages.forEach(function (image) {
          if ( image.offsetTop <= document.documentElement.clientHeight + document.documentElement.scrollTop) {
            image.src = image.getAttribute("data-src");
          }
        });
      }

添加滾動(dòng)條監(jiān)聽(tīng)。

  window.onscroll = function () {
        lazyload();
};

html結(jié)構(gòu)。

 <img src="./default.gif" class="lazyload" data-src="./photo-1.jpg" />

優(yōu)化

上面只是簡(jiǎn)單的實(shí)現(xiàn)圖片懶加載,在實(shí)際開(kāi)發(fā)中還要很多細(xì)節(jié)需要優(yōu)化: 首先是兼容性,這里有兩個(gè)點(diǎn)涉及到兼容性:document.documentElement.clientHeightdocument.documentElement.scrollTop 。 獲取瀏覽器窗口的內(nèi)部高度方法有 window.innerHeight、document.documentElement.clientHeight。 window.innerHeight兼容性是 ie9+ 和其他主流瀏覽器。

document.documentElement.clientHeight 瀏覽器都支持。

獲取滾動(dòng)位置方法有 window.pageYOffsetdocument.documentElement.scrollTop 。 window.pageYOffset 兼容性是 ie9+ 和其他主流瀏覽器。

第二優(yōu)化點(diǎn)是offsetTop。

offsetParent 元素有滾動(dòng)條的情況下計(jì)算會(huì)不會(huì)有問(wèn)題

HTMLElement.offsetTop 為只讀屬性,它返回當(dāng)前元素相對(duì)于其 offsetParent 元素的頂部?jī)?nèi)邊距的距離。 ——MDN

offsetTop 是相對(duì)其 offsetParent 元素的并不是相對(duì)瀏覽器窗口可視區(qū)域的。如果圖片元素有 offsetParent 那么 offsetTop 是有偏差的。

      function getBoundingClientTop(el) {
        let top = el.offsetTop;
        let parent = el.offsetParent;
        while (parent) {
          top += parent.offsetTop;
          parent = parent.offsetParent;
        }
        return top;
      }

第三優(yōu)化點(diǎn)避免賦值 src 。 代碼是通過(guò) lazyload 類(lèi)獲取需要懶加載的元素,這樣會(huì)把之前已經(jīng)加載圖片的元素也獲取到了,而重復(fù)設(shè)置 src屬性。

   function lazyload() {
        var lazyImages = document.querySelectorAll(".lazyload[data-src]");
        lazyImages.forEach(function (image) {
          if (
            getBoundingClientTop(image) <=
            document.documentElement.clientHeight +
              document.documentElement.scrollTop
          ) {
            image.src = image.getAttribute("data-src");
            image.removeAttribute("data-src")
          }
        });
      }

通過(guò) lazyload 類(lèi)并且有 data-src 來(lái)獲取元素,src 設(shè)置完后移除 data-src 屬性來(lái)避免重復(fù)設(shè)置 src 。

第四優(yōu)化點(diǎn) onscroll 是否添加防抖。 onscroll 常用的優(yōu)化點(diǎn)是加入防抖來(lái)減少事件觸發(fā)的頻率,但這里如果加了防抖,計(jì)算元素是否在可視區(qū)域內(nèi)的精度就差很多,當(dāng)滾動(dòng)速度比較快的情況下加載反應(yīng)不靈敏,這里就要找平衡點(diǎn)。

第五優(yōu)化點(diǎn)頁(yè)面中局部的 div 滾動(dòng)圖片懶加載。 除了整個(gè)頁(yè)面的滾動(dòng)圖片懶加載,也有頁(yè)面中局部滾動(dòng)圖片懶加載,就需要給制定的有滾動(dòng)條 dom 元素綁定onscroll 事件。

    srcollDom.onscroll = function () {
        lazyload();
      };

并且獲取圖片 top 是相對(duì)有滾動(dòng)條 dom 元素

getBoundingClientTop(image)-getBoundingClientTop(srcollDom) <= srcollDom.clientHeight + srcollDom.scrollTop

第六優(yōu)化點(diǎn)加載圖片的時(shí)間點(diǎn)提前。 代碼中是圖片元素進(jìn)入可視區(qū)域后才加載圖片,用戶(hù)就需要等待一段時(shí)間才能看到圖片顯示出來(lái),如果把圖片加載時(shí)間提前,圖片元素距離可視區(qū)域一定范圍內(nèi)就加載圖片,那么用戶(hù)等待時(shí)間就會(huì)減少一些。

優(yōu)點(diǎn)

兼容性好,各個(gè)環(huán)節(jié)可以控制。

缺點(diǎn)

性能相對(duì)不是很好,滾動(dòng)事件頻繁觸發(fā),并且獲取元素的位置信息,可能會(huì)強(qiáng)行觸發(fā)重排和重繪導(dǎo)致一定的性能消耗。

方案三:通過(guò)getBoundingClientRect來(lái)計(jì)算是否在可視區(qū)域內(nèi)

大致思路和方案二一樣只是把獲取圖片元素 offsetTop 改成 getBoundingClientRect 方法獲取離可視區(qū)頂端的距離。比方案一要簡(jiǎn)單一點(diǎn),缺點(diǎn)也和方案一一樣。

方案四:使用IntersectionObserver來(lái)判斷是否在可視區(qū)域內(nèi)

該方案是通過(guò) IntersectionObserver 來(lái)判斷圖片元素是否在可視區(qū)域內(nèi)。

IntersectionObserver() 構(gòu)造器創(chuàng)建并返回一個(gè) IntersectionObserver 對(duì)象。如果指定 rootMargin 則會(huì)檢查其是否符合語(yǔ)法規(guī)定,檢查閾值以確保全部在 0.0 到 1.0 之間,并且閾值列表會(huì)按升序排列。如果閾值列表為空,則默認(rèn)為一個(gè) [0.0] 的數(shù)組。 —— MDN

完全沒(méi)看懂mdn的這段解釋?zhuān)?jiǎn)單說(shuō)就是

IntersectionObserver 接口提供了一種異步觀察目標(biāo)元素與祖先元素或頂級(jí)文檔 viewport 的交集中的變化的方法。祖先元素與視窗viewport被稱(chēng)為根(root)。

      var images = document.querySelectorAll(".lazyload");
      var io = new IntersectionObserver(
        function (entries) {
          entries.forEach((item) => {
            // isIntersecting是一個(gè)Boolean值,判斷目標(biāo)元素當(dāng)前是否可見(jiàn)
            if (item.isIntersecting) {
              item.target.src = item.target.dataset.src;
              // 圖片加載后即停止監(jiān)聽(tīng)該元素
              io.unobserve(item.target);
            }
          });
        }
      );
      images.forEach(function(image){
        io.observe(image)
      })

IntersectionObserver 的監(jiān)聽(tīng)對(duì)于頁(yè)面局部滾動(dòng)條也是有效的,不用再單獨(dú)對(duì)局部滾動(dòng)條進(jìn)行處理。 而提前加載圖片通過(guò)配置 rootMargin 來(lái)擴(kuò)大監(jiān)聽(tīng)區(qū)域。

      var images = document.querySelectorAll(".lazyload");
      var io = new IntersectionObserver(
        function (entries) {
          entries.forEach((item) => {
            // isIntersecting是一個(gè)Boolean值,判斷目標(biāo)元素當(dāng)前是否可見(jiàn)
            if (item.isIntersecting) {
              item.target.src = item.target.dataset.src;
              // 圖片加載后即停止監(jiān)聽(tīng)該元素
              io.unobserve(item.target);
            }
          });
        },
        {
          root: null,
          rootMargin: "0px 0px 300px 0px",
        }
      );
      images.forEach(function(image){
        io.observe(image);
      })

兼容性

在一些低版本瀏覽器中還存在一些問(wèn)題。

優(yōu)點(diǎn)

性能好,簡(jiǎn)單。

缺點(diǎn)

低版本瀏覽器 IntersectionObserver 存在問(wèn)題,不支持 IE。

問(wèn)題

布局抖動(dòng)

布局抖動(dòng)是因?yàn)殚_(kāi)始圖片沒(méi)有寬高,內(nèi)容顯示出來(lái)后有了寬高導(dǎo)致位置變動(dòng)。帶來(lái)的影響主要是用戶(hù)體驗(yàn)不好,用戶(hù)的注意力已經(jīng)鎖定了某個(gè)區(qū)域準(zhǔn)備閱讀,突然那個(gè)區(qū)域下移了,中斷閱讀而重新定位。可以直接在 img 標(biāo)簽上設(shè)置要加載圖片的寬高。

<img src="blank.gif" data-src="normal.jpg" style="width:800px;height:600px;" />

解決問(wèn)題:方案解決的問(wèn)題范圍是圖片寬高固定的情況,在響應(yīng)式環(huán)境圖片寬高不確定下不適用。

用戶(hù)體驗(yàn):img的默認(rèn)占位圖是一個(gè)loading或是灰色背景,圖片還沒(méi)加載的體驗(yàn)。

響應(yīng)式圖片

雖然響應(yīng)式下圖片的寬高會(huì)變,但是圖片的寬高比是不變的,圖片的寬高比變了圖片也就變形了。所以 img 標(biāo)簽設(shè)定圖片寬高比,就能根據(jù)不同視圖的寬度算出不同高度。 先創(chuàng)建一個(gè)寬高比為 5:1 的 div。

   <div style="padding-bottom: 20%;background-color: green;"></div>

padding 為百分比是相對(duì)自身寬度的百分比。 然后再創(chuàng)建了一個(gè)寬高比為 5:1 的 img。

    <div style="padding-bottom: 20%;position: relative;">
        <img style="position:absolute;width: 100%;height:100%">
      </div>

這樣就能適應(yīng)響應(yīng)式的寬度改變,這種方式叫 Aspect Ratio Boxes。 占位圖片可以設(shè)置成原圖片的小尺寸圖片,被放大后圖片變模糊,這樣開(kāi)始加載小圖片但圖片的輪廓出現(xiàn),后面在加載大圖片顯示清晰,給用戶(hù)的體驗(yàn)是圖片開(kāi)始就在加載,然后加載完成就變清晰了。 img 標(biāo)簽 srcset 屬性是處理響應(yīng)式圖片的。懶加載中可以設(shè)置 data-srcset 來(lái)延遲修改 srcset 屬性。

SEO不友好

<img> 標(biāo)簽中的 src 屬性攜帶的仍然是原始大小的圖片確保了站外 SEO、社會(huì)化分享、RSS 等不會(huì)讀不到原圖。Aspect Ratio Boxes 方式使占位圖片適應(yīng)響應(yīng)式,srcset 屬性存放了一張?jiān)瓐D的小尺寸縮略圖阻止 src 原圖的加載而加載縮略圖優(yōu)化加載體驗(yàn),最后延遲將 data-srcset 的值賦值到 srcset 中。

插件

  • lazyload.js 是 IntersectionObserver 方式,而且當(dāng)瀏覽器不支持 IntersectionObserver 的時(shí)候就直接加載圖片,沒(méi)有延遲加載的功能。
  • vue-lazyload 使用 IntersectionObserver 和 getBoundingClientRect 方式,默認(rèn) getBoundingClientRect 方式懶加載,里面的一些封裝細(xì)節(jié)有很多有意思的地方,不止綁定了 onscroll 事件還綁定了 'onwheel'、'onmousewheel'、'onresize'、 'onanimationend'、'ontransitionend'、'ontouchmove'問(wèn)什么要綁定這么多事件,插件為什么默認(rèn) getBoundingClientRect 方式而不用 IntersectionObserver 方式,待下回分解。
  • react-lazyload 只用了 getBoundingClientRect 方式,里面的封裝細(xì)節(jié)也很有意思,待下回分解。

以上就是前端JS圖片懶加載原理方案詳解的詳細(xì)內(nèi)容,更多關(guān)于JS圖片懶加載原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 四十九個(gè)javascript小知識(shí)實(shí)用技巧

    四十九個(gè)javascript小知識(shí)實(shí)用技巧

    這篇文章主要給大家分享得是四十九個(gè)javascript小知識(shí)實(shí)用技巧像下面文章圍繞JavaScript得各種技巧詳細(xì)介紹,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2021-11-11
  • 微信小程序 自動(dòng)登陸PHP源碼實(shí)例(源碼下載)

    微信小程序 自動(dòng)登陸PHP源碼實(shí)例(源碼下載)

    這篇文章主要介紹了微信小程序 自動(dòng)登陸PHP源碼實(shí)例并且附有源碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • javascript對(duì)象3個(gè)屬性特征

    javascript對(duì)象3個(gè)屬性特征

    這篇文章主要介紹了javascript對(duì)象3個(gè)屬性特征,writable可寫(xiě)、enumerable可枚舉、configurable可配置,下面來(lái)看看文章的詳細(xì)內(nèi)容吧
    2021-11-11
  • JavaScript?history?對(duì)象詳解

    JavaScript?history?對(duì)象詳解

    這篇文章主要介紹了JavaScript?history?對(duì)象詳解,history?對(duì)象表示當(dāng)前窗口首次使用以來(lái)用戶(hù)的導(dǎo)航歷史記錄。因?yàn)?history?是?window?的屬性,所以每個(gè)?window?都有自己的?history?對(duì)象,更多詳細(xì)內(nèi)容請(qǐng)參考下面文章內(nèi)容
    2021-11-11
  • JS深入淺出Function與構(gòu)造函數(shù)

    JS深入淺出Function與構(gòu)造函數(shù)

    這篇文章主要介紹了JS深入淺出Function與構(gòu)造函數(shù),F(xiàn)unction是一個(gè)構(gòu)造函數(shù),可以通過(guò)該構(gòu)造函數(shù)去創(chuàng)建一個(gè)函數(shù),創(chuàng)建的函數(shù)是一個(gè)Function對(duì)象,具體內(nèi)容請(qǐng)參考下面文章的詳細(xì)內(nèi)容,需要的朋友可以參考一下
    2021-12-12
  • html下載本地

    html下載本地

    html下載本地...
    2006-06-06
  • Fabric.js 保存自定義屬性方法示例

    Fabric.js 保存自定義屬性方法示例

    這篇文章主要為大家介紹了Fabric.js 保存自定義屬性方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 微信小程序 wxapp畫(huà)布 canvas詳細(xì)介紹

    微信小程序 wxapp畫(huà)布 canvas詳細(xì)介紹

    這篇文章主要介紹了微信小程序 wxapp畫(huà)布 canvas的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • JS版的date函數(shù)(和PHP的date函數(shù)一樣)

    JS版的date函數(shù)(和PHP的date函數(shù)一樣)

    這篇文章主要介紹了JS版的date函數(shù),使用方法和PHP的date函數(shù)一樣,需要的朋友可以參考下
    2014-05-05
  • 微信小程序 wx.uploadFile無(wú)法上傳解決辦法

    微信小程序 wx.uploadFile無(wú)法上傳解決辦法

    這篇文章主要介紹了微信小程序 wx.uploadFile無(wú)法上傳解決辦法的相關(guān)資料,需要的朋友可以參考下
    2016-12-12

最新評(píng)論

武平县| 房产| 秦安县| 黑水县| 洛川县| 苏尼特左旗| 壶关县| 三门县| 柏乡县| 长汀县| 沂南县| 仲巴县| 兴国县| 武宣县| 苏州市| 富平县| 侯马市| 汝城县| 教育| 阿勒泰市| 乃东县| 洪泽县| 阳新县| 天祝| 永川市| 朝阳市| 雅安市| 灵台县| 双鸭山市| 贡山| 东光县| 吕梁市| 铁岭市| 同德县| 巨鹿县| 平陆县| 平定县| 舟曲县| 康马县| 肇州县| 平陆县|