整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端開發,原生 JS 實現最簡單的圖片懶加載

          前端開發,原生 JS 實現最簡單的圖片懶加載


          加載


          什么是懶加載

          懶加載其實就是延遲加載,是一種對網頁性能優化可方式,比如當訪問一個頁面的時候,優先顯示可視區域的圖片而不一次性加載所有圖片,當需要顯示的時候再發送圖片請求,避免打開網頁時加載過多資源。

          什么時候用懶加載

          當頁面中需要一次性載入很多圖片的時候,往往都是需要用懶加載的。

          懶加載原理

          我們都知道HTML中的 <img>標簽是代表文檔中的一個圖像。。說了個廢話。。

          <img>標簽有一個屬性是 src,用來表示圖像的URL,當這個屬性的值不為空時,瀏覽器就會根據這個值發送請求。如果沒有 src屬性,就不會發送請求。

          嗯?貌似這點可以利用一下?

          我先不設置 src,需要的時候再設置?

          nice,就是這樣。

          我們先不給 <img>設置 src,把圖片真正的URL放在另一個屬性 data-src中,在需要的時候也就是圖片進入可視區域的之前,將URL取出放到 src中。

          實現


          HTML結構

          <div class="container">
           <div class="img-area">
             <img class="my-photo" alt="loading" data-src="./img/img1.png">
           </div>
           <div class="img-area">
             <img class="my-photo" alt="loading" data-src="./img/img2.png">
           </div>
           <div class="img-area">
             <img class="my-photo" alt="loading" data-src="./img/img3.png">
           </div>
           <div class="img-area">
             <img class="my-photo" alt="loading" data-src="./img/img4.png">
           </div>
           <div class="img-area">
             <img class="my-photo" alt="loading" data-src="./img/img5.png">
           </div>
          </div>

          仔細觀察一下, <img>標簽此時是沒有 src屬性的,只有 alt和 data-src屬性。

          alt 屬性是一個必需的屬性,它規定在圖像無法顯示時的替代文本。 data-* 全局屬性:構成一類名稱為自定義數據屬性的屬性,可以通過 HTMLElement.dataset來訪問。

          如何判斷元素是否在可視區域

          方法一

          網上看到好多這種方法,稍微記錄一下。

          1. 通過 document.documentElement.clientHeight獲取屏幕可視窗口高度
          2. 通過 document.documentElement.scrollTop獲取瀏覽器窗口頂部與文檔頂部之間的距離,也就是滾動條滾動的距離
          3. 通過 element.offsetTop獲取元素相對于文檔頂部的距離

          然后判斷②-③<①是否成立,如果成立,元素就在可視區域內。

          方法二(推薦)

          通過 getBoundingClientRect()方法來獲取元素的大小以及位置,MDN上是這樣描述的:

          The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

          這個方法返回一個名為 ClientRect的 DOMRect對象,包含了 top、 right、 botton、 left、 width、 height這些值。

          MDN上有這樣一張圖:

          可以看出返回的元素位置是相對于左上角而言的,而不是邊距。

          我們思考一下,什么情況下圖片進入可視區域。

          假設 constbound=el.getBoundingClientRect();來表示圖片到可視區域頂部距離; 并設 constclientHeight=window.innerHeight;來表示可視區域的高度。

          隨著滾動條的向下滾動, bound.top會越來越小,也就是圖片到可視區域頂部的距離越來越小,當 bound.top===clientHeight時,圖片的上沿應該是位于可視區域下沿的位置的臨界點,再滾動一點點,圖片就會進入可視區域。

          也就是說,在 bound.top<=clientHeight時,圖片是在可視區域內的。

          我們這樣判斷:

          function isInSight(el) {
           const bound=el.getBoundingClientRect();
           const clientHeight=window.innerHeight;
           //如果只考慮向下滾動加載
           //const clientWidth=window.innerWeight;
           return bound.top <=clientHeight + 100;
          }

          這里有個+100是為了提前加載。

          加載圖片

          頁面打開時需要對所有圖片進行檢查,是否在可視區域內,如果是就加載。

          function checkImgs() {
           const imgs=document.querySelectorAll('.my-photo');
           Array.from(imgs).forEach(el=> {
             if (isInSight(el)) {
               loadImg(el);
             }
           })
          }
          
          function loadImg(el) {
           if (!el.src) {
             const source=el.dataset.src;
             el.src=source;
           }
          }

          這里應該是有一個優化的地方,設一個標識符標識已經加載圖片的index,當滾動條滾動時就不需要遍歷所有的圖片,只需要遍歷未加載的圖片即可。

          函數節流

          在類似于滾動條滾動等頻繁的DOM操作時,總會提到“函數節流、函數去抖”。

          所謂的函數節流,也就是讓一個函數不要執行的太頻繁,減少一些過快的調用來節流。

          基本步驟:

          1. 獲取第一次觸發事件的時間戳
          2. 獲取第二次觸發事件的時間戳
          3. 時間差如果大于某個閾值就執行事件,然后重置第一個時間
          function throttle(fn, mustRun=500) {
           const timer=null;
           let previous=null;
           return function() {
             const now=new Date();
             const context=this;
             const args=arguments;
             if (!previous){
               previous=now;
             }
             const remaining=now - previous;
             if (mustRun && remaining >=mustRun) {
               fn.apply(context, args);
               previous=now;
             }
           }
          }

          這里的 mustRun就是調用函數的時間間隔,無論多么頻繁的調用 fn,只有 remaining>=mustRun時 fn才能被執行。

          實驗


          頁面打開時

          可以看出此時僅僅是加載了img1和img2,其它的img都沒發送請求,看看此時的瀏覽器

          第一張圖片是完整的呈現了,第二張圖片剛進入可視區域,后面的就看不到了~

          頁面滾動時

          當我向下滾動,此時瀏覽器是這樣

          此時第二張圖片完全顯示了,而第三張圖片顯示了一點點,這時候我們看看請求情況

          img3的請求發出來,而后面的請求還是沒發出~

          全部載入時

          當滾動條滾到最底下時,全部請求都應該是發出的,如圖

          更新


          方法三 IntersectionObserver

          經大佬提醒,發現了這個方法

          先附上鏈接:

          jjc大大:

          https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10

          阮一峰大大:

          http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

          API Sketch for Intersection Observers:

          https://github.com/WICG/IntersectionObserver

          IntersectionObserver可以自動觀察元素是否在視口內。

          var io=new IntersectionObserver(callback, option);
          // 開始觀察
          io.observe(document.getElementById('example'));
          // 停止觀察
          io.unobserve(element);
          // 關閉觀察器
          io.disconnect();

          callback的參數是一個數組,每個數組都是一個 IntersectionObserverEntry對象,包括以下屬性:

          屬性描述time可見性發生變化的時間,單位為毫秒rootBounds與getBoundingClientRect()方法的返回值一樣boundingClientRect目標元素的矩形區域的信息intersectionRect目標元素與視口(或根元素)的交叉區域的信息intersectionRatio目標元素的可見比例,即intersectionRect占boundingClientRect的比例,完全可見時為1,完全不可見時小于等于0target被觀察的目標元素,是一個 DOM 節點對象

          我們需要用到 intersectionRatio來判斷是否在可視區域內,當 intersectionRatio>0&&intersectionRatio<=1即在可視區域內。

          代碼

          function checkImgs() {
           const imgs=Array.from(document.querySelectorAll(".my-photo"));
           imgs.forEach(item=> io.observe(item));
          }
          
          function loadImg(el) {
           if (!el.src) {
             const source=el.dataset.src;
             el.src=source;
           }
          }
          
          const io=new IntersectionObserver(ioes=> {
           ioes.forEach(ioe=> {
             const el=ioe.target;
             const intersectionRatio=ioe.intersectionRatio;
             if (intersectionRatio > 0 && intersectionRatio <=1) {
               loadImg(el);
             }
             el.onload=el.onerror=()=> io.unobserve(el);
           });
          });

          源自:segmentfault

          聲明:文章著作權歸作者所有,如有侵權,請聯系小編刪除。

          于一些擁有大量圖片的網站來說,如果一個頁面有超過 50 張圖片,就會造成網站頁面加載太慢以及移動端耗費流量太大。就像前幾天給理想做的作品頁面,頁面上至少200張圖,為了解決這樣的問題,可以使用LazyLoad按需加載,又稱懶加載。

          LazyLoad按需加載

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          什么是LazyLoad按需加載

          LazyLoad按需加載采用圖片按需加載技術,打開頁面時只會加載首屏圖片。訪客往下滾動時才會陸續加載需要展現的圖片,這樣非常高效,體驗舒適。

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          LazyLoad按需加載實現方法

          我們在自己做網站時,也可以實現LazyLoad按需加載,增強網站的用戶體驗。下面學做網站論壇就來介紹一下LazyLoad按需加載實現方法。(以下會使用到HTML代碼,如果對代碼不熟悉,可以學習一下html視頻教程)

          引入LazyLoad按需加載必須的二個文件:jquery.js和jquery.lazyload.js。引入方法很簡單,只需將下面的代碼放到</head>標簽上方即可;

          <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script src="https://raw.githubusercontent.com/tuupola/jquery_lazyload/master/jquery.lazyload.min.js"></script>

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          網站上所有圖片都使用以下的格式書寫:

          <img class="lazy" src="" data-original="圖片地址" width="100" height="100" alt="">

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          在網站的</body>標簽上面,放上以下的JS代碼,來實現LazyLoad按需加載(懶加載);

          <script type="text/javascript">$(function() { $("img.lazy").lazyload({ threshold : 200, // 設置閥值 effect : "fadeIn" // 設置圖片漸入特效 });});</script>

          做個筆記,圖片如何實現懶加載(LazyLoad按需加載)

          這樣,我們自己在建網站時,也可以輕松實現LazyLoad按需加載(懶加載)了。快去自己的網站上試試吧!

          網站典型負載中,圖像和視頻是非常重要的一部分內容。 不過遺憾的是,項目干系人并不愿意從其現有應用中刪除任何媒體資源。 這種僵局不免令人沮喪,尤其是當所有相關方都希望改善網站性能,但卻無法就具體方法達成一致時。 幸運的是,延遲加載解決方案可以減少初始頁面負載_和_加載時間,但不會刪減任何內容。

          什么是延遲加載?

          延遲加載是一種在加載頁面時,延遲加載非關鍵資源的方法, 而這些非關鍵資源則在需要時才進行加載。 就圖像而言,“非關鍵”通常是指“屏幕外”。 如果您曾使用過 Lighthouse 并檢驗過其提供的改進機會,就有可能從 屏幕外圖像審核中看到一些這方面的指導:

          您可能已經見過延遲加載的實際應用,其過程大致如下:

          • 您訪問一個頁面,并開始滾動閱讀內容。
          • 在某個時刻,您將占位符圖像滾動到視口中。
          • 該占位符圖像瞬間替換為最終圖像。

          您可以在熱門發布平臺 Medium 上找到圖像延遲加載的示例。該平臺在加載頁面時會先加載輕量級的占位符圖像,并在其滾動到視口時,將之替換為延遲加載的圖像。

          圖像延遲加載實際應用示例。 占位符圖像在頁面加載時加載(左側),當滾動到視口時,最終圖像隨即加載(即在需要時加載)。

          為何要延遲加載圖像或視頻,而不是直接_加載_?

          因為直接加載可能會加載用戶永遠不會查看的內容, 進而導致一些問題:

          • 浪費數據流量。 如果使用無限流量網絡,這可能還不是最壞的情況(不過,這些寶貴的帶寬原本可以用來下載用戶確實會查看的其他資源)。 但如果流量有限,加載用戶永遠不會查看的內容實際上是在浪費用戶的金錢。
          • 浪費處理時間、電池電量和其他系統資源。 下載媒體資源后,瀏覽器必須將其解碼,并在視口中渲染其內容。
          • 延遲加載圖像和視頻時,可以減少初始頁面加載時間、初始頁面負載以及系統資源使用量,所有這一切都會對性能產生積極影響。 在本指南中,我們將針對延遲加載圖像和視頻提供一些技巧及指導,并列舉幾個常用的庫。

          延遲加載圖像

          內聯圖像

          <img> 元素中使用的圖像是最常見的延遲加載對象。 延遲加載 <img> 元素時,我們使用 JavaScript 來檢查其是否在視口中。 如果元素在視口中,則其 src(有時是 srcset)屬性中就會填充所需圖像內容的網址。

          使用 Intersection Observer

          如果您曾經編寫過延遲加載代碼,您可能是使用 scroll 或 resize 等事件處理程序來完成任務。 雖然這種方法在各瀏覽器之間的兼容性最好,但是現代瀏覽器支持通過 Intersection Observer API 來檢查元素的可見性,這種方式的性能和效率更好。

          注:并非所有瀏覽器都支持 Intersection Observer。 如果瀏覽器之間的兼容性至關重要,請務必閱讀下一節,其中會說明如何使用性能稍差(但兼容性更好!)的 scroll 和 resize 事件處理程序來延遲加載圖像。

          與依賴于各種事件處理程序的代碼相比,Intersection Observer 更容易使用和閱讀。這是因為開發者只需要注冊一個 Observer 即可監視元素,而無需編寫冗長的元素可見性檢測代碼。 然后,開發者只需要決定元素可見時需要做什么即可。 假設我們的延遲加載

          元素采用以下基本標記模式:

          <img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
          

          在此標記中,我們應關注三個相關部分:

          • class 屬性,這是我們在 JavaScript 中選擇元素時要使用的類選擇器。
          • src 屬性,引用頁面最初加載時顯示的占位符圖像。
          • data-src 和 data-srcset 屬性,屬于占位符屬性,其中包含元素進入視口后要加載的圖像的網址。

          現在,我們來看看如何在 JavaScript 中使用 Intersection Observer,并通過以下標記模式延遲加載圖像:

          document.addEventListener("DOMContentLoaded", function() {
           var lazyImages=[].slice.call(document.querySelectorAll("img.lazy"));
           if ("IntersectionObserver" in window) {
           let lazyImageObserver=new IntersectionObserver(function(entries, observer) {
           entries.forEach(function(entry) {
           if (entry.isIntersecting) {
           let lazyImage=entry.target;
           lazyImage.src=lazyImage.dataset.src;
           lazyImage.srcset=lazyImage.dataset.srcset;
           lazyImage.classList.remove("lazy");
           lazyImageObserver.unobserve(lazyImage);
           }
           });
           });
           lazyImages.forEach(function(lazyImage) {
           lazyImageObserver.observe(lazyImage);
           });
           } else {
           // Possibly fall back to a more compatible method here
           }
          });
          

          在文檔的 DOMContentLoaded 事件中,此腳本會查詢 DOM,以獲取類屬性為 lazy 的所有 <img> 元素。 如果 Intersection Observer 可用,我們會創建一個新的 Observer,以在 img.lazy 元素進入視口時運行回調。 請參閱此 CodePen 示例,查看代碼的實際運行情況.

          注:此代碼使用名為 isIntersecting 的 Intersection Observer 方法,該方法在 Edge 15 的 Intersection Observer 實現中不可用。 因此,以上延遲加載代碼(以及其他類似的代碼片段)將會失敗。 請查閱此 GitHub 問題,以獲取有關更完整的功能檢測條件的指導。

          不過,雖然Intersection Observer在瀏覽器之間獲得良好的支持,但并非所有瀏覽器皆提供支持。 對于不支持 Intersection Observer 的瀏覽器,您可以使用 polyfill,或者如以上代碼所述,檢測 Intersection Observer 是否可用,并在其不可用時回退到兼容性更好的舊方法。

          使用事件處理程序(兼容性最好的方法)

          雖然您_應該_使用 Intersection Observer 來執行延遲加載,但您的應用可能對瀏覽器的兼容性要求比較嚴格。 您_可以_使用 polyfil 為不支持 Intersection Observer 的瀏覽器提供支持(這種方法最簡單),但也可以回退到使用 scroll 和 resize的代碼,甚至回退到與 getBoundingClientRect 配合使用的 orientationchange 事件處理程序,以確定元素是否在視口中。

          document.addEventListener("DOMContentLoaded", function() {
           let lazyImages=[].slice.call(document.querySelectorAll("img.lazy"));
           let active=false;
           const lazyLoad=function() {
           if (active===false) {
           active=true;
           setTimeout(function() {
           lazyImages.forEach(function(lazyImage) {
           if ((lazyImage.getBoundingClientRect().top <=window.innerHeight && lazyImage.getBoundingClientRect().bottom >=0) && getComputedStyle(lazyImage).display !=="none") {
           lazyImage.src=lazyImage.dataset.src;
           lazyImage.srcset=lazyImage.dataset.srcset;
           lazyImage.classList.remove("lazy");
           lazyImages=lazyImages.filter(function(image) {
           return image !==lazyImage;
           });
           if (lazyImages.length===0) {
           document.removeEventListener("scroll", lazyLoad);
           window.removeEventListener("resize", lazyLoad);
           window.removeEventListener("orientationchange", lazyLoad);
           }
           }
           });
           active=false;
           }, 200);
           }
           };
           document.addEventListener("scroll", lazyLoad);
           window.addEventListener("resize", lazyLoad);
           window.addEventListener("orientationchange", lazyLoad);
          });
          

          此代碼在 scroll 事件處理程序中使用 getBoundingClientRect 來檢查是否有任何 img.lazy 元素處于視口中。 使用 setTimeout 調用來延遲處理,active 變量則包含處理狀態,用于限制函數調用。 延遲加載圖像時,這些元素隨即從元素數組中移除。 當元素數組的 length 達到 0 時,滾動事件處理程序代碼隨即移除。 您可在此 CodePen 示例中,查看代碼的實際運行情況。

          雖然此代碼幾乎可在任何瀏覽器中正常運行,但卻存在潛在的性能問題,即重復的 setTimeout 調用可能純屬浪費,即使其中的代碼受限制,它們仍會運行。 在此示例中,當文檔滾動或窗口調整大小時,不管視口中是否有圖像,每 200 毫秒都會運行一次檢查。 此外,跟蹤尚未延遲加載的元素數量,以及取消綁定滾動事件處理程序的繁瑣工作將由開發者來完成。

          簡而言之:請盡可能使用 Intersection Observer,如果應用有嚴格的兼容性要求,則回退到事件處理程序。

          CSS 中的圖像

          雖然 <img> 標記是在網頁上使用圖像的最常見方式,但也可以通過 CSS background-image 屬性(以及其他屬性)來調用圖像。 與加載時不考慮可見性的 <img> 元素不同,CSS 中的圖像加載行為是建立在更多的推測之上。 構建文檔和 CSS 對象模型以及渲染 樹后,瀏覽器會先檢查 CSS 以何種方式應用于文檔,再請求外部資源。 如果瀏覽器確定涉及某外部資源的 CSS 規則不適用于當前構建中的文檔,則瀏覽器不會請求該資源。

          這種推測性行為可用來延遲 CSS 中圖像的加載,方法是使用 JavaScript 來確定元素在視口內,然后將一個類應用于該元素,以應用調用背景圖像的樣式。 如此即可在需要時而非初始加載時下載圖像。 例如,假定一個元素中包含大型主角背景圖片:

          <div class="lazy-background">
           <h1>Here's a hero heading to get your attention!</h1>
           <p>Here's hero copy to convince you to buy a thing!</p>
           <a href="/buy-a-thing">Buy a thing!</a>
          </div>
          

          div.lazy-background 元素通常包含由某些 CSS 調用的大型主角背景圖片。 但是,在此延遲加載示例中,我們可以通過 visible 類來隔離 div.lazy-background 元素的 background-image 屬性,而且我們會在元素進入視口時對其添加這個類:

          .lazy-background {
           background-image: url("hero-placeholder.jpg"); /* Placeholder image */
          }
          .lazy-background.visible {
           background-image: url("hero.jpg"); /* The final image */
          }
          

          我們將從這里使用 JavaScript 來檢查該元素是否在視口內(通過 Intersection Observer 進行檢查!),如果在視口內,則對 div.lazy-background 元素添加 visible 類以加載該圖像:

          document.addEventListener("DOMContentLoaded", function() {
           var lazyBackgrounds=[].slice.call(document.querySelectorAll(".lazy-background"));
           if ("IntersectionObserver" in window) {
           let lazyBackgroundObserver=new IntersectionObserver(function(entries, observer) {
           entries.forEach(function(entry) {
           if (entry.isIntersecting) {
           entry.target.classList.add("visible");
           lazyBackgroundObserver.unobserve(entry.target);
           }
           });
           });
           lazyBackgrounds.forEach(function(lazyBackground) {
           lazyBackgroundObserver.observe(lazyBackground);
           });
           }
          });
          

          如上文所述,由于并非所有瀏覽器都支持 Intersection Observer,因此您需要確保提供回退方案或 polyfill。 請參閱此 CodePen 演示,查看代碼的實際運行情況。

          延遲加載視頻

          與圖像元素一樣,視頻也可以延遲加載。 在正常情況下加載視頻時,我們使用的是 <video> 元素(盡管也可以改為使用 <img>,不過實現方式受限)。 但是,延遲加載 <video> 的_方式_取決于用例。 下文探討的幾種情況所需的解決方案均不相同。

          視頻不自動播放

          對于需要由用戶啟動播放的視頻(即_不_自動播放的視頻),最好指定 <video> 元素的 preload 屬性:

          <video controls preload="none" poster="one-does-not-simply-placeholder.jpg">
           <source src="one-does-not-simply.webm" type="video/webm">
           <source src="one-does-not-simply.mp4" type="video/mp4">
          </video>
          

          這里,我們使用值為 none 的 preload 屬性來阻止瀏覽器預加載_任何_視頻數據。 為占用空間,我們使用 poster 屬性為 <video> 元素提供占位符。 這是因為默認的視頻加載行為可能會因瀏覽器不同而有所不同:

          • 在 Chrome 中,之前的 preload 默認值為 auto,但從 Chrome 64 開始,默認值變為 metadata。 雖然如此,在 Chrome 桌面版中,可能會使用 Content-Range 標頭預加載視頻的部分內容。 Firefox、Edge 和 Internet Explorer 11 的行為與此相似。
          • 與 Chrome 桌面版相同,Safari 11.0 桌面版會預加載視頻的部分內容, 而 11.2 版(目前為 Safari 的 Tech Preview 版)僅預加載視頻元數據。 iOS 版 Safari 不會 預加載視頻。
          • 啟用流量節省程序模式后,preload 默認為 none。

          由于瀏覽器在 preload 方面的默認行為并非一成不變,因此您最好明確指定該行為。 在由用戶啟動播放的情況下,使用 preload="none" 是在所有平臺上延遲加載視頻的最簡單方法。 但 preload 屬性并非延遲加載視頻內容的唯一方法。 利用視頻 預加載快速播放或許能提供一些想法和見解,助您了解如何通過 JavaScript 播放視頻。

          但遺憾的是,當我們想使用視頻代替動畫 GIF 時,事實證明以上方法無效。我們將在下文介紹相關內容。

          用視頻代替動畫 GIF

          雖然動畫 GIF 應用廣泛,但其在很多方面的表現均不如視頻,尤其是在輸出文件大小方面。 動畫 GIF 的數據大小可達數兆字節, 而視覺效果相當的視頻往往小得多。

          使用 <video> 元素代替動畫 GIF 并不像使用 <img> 元素那么簡單。 動畫 GIF 具有以下三種固有行為:

          • 加載時自動播放。
          • 連續循環播放(但并非始終如此)。
          • 沒有音軌。

          使用 <video> 元素進行替代類似于:

          <video autoplay muted loop playsinline>
           <source src="one-does-not-simply.webm" type="video/webm">
           <source src="one-does-not-simply.mp4" type="video/mp4">
          </video>
          

          autoplay、muted 和 loop 屬性的含義不言而喻,而 playsinline 是在 iOS 中進行自動播放所必需。 現在,我們有了可以跨平臺使用的“視頻即 GIF”替代方式。 但是,如何進行延遲加載?Chrome 會自動延遲加載視頻,但并不是所有瀏覽器都會提供這種優化行為。 根據您的受眾和應用要求,您可能需要自己手動完成這項操作。 首先,請相應地修改 <video> 標記:

          <video autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
           <source data-src="one-does-not-simply.webm" type="video/webm">
           <source data-src="one-does-not-simply.mp4" type="video/mp4">
          </video>
          

          您會發現添加了 poster 屬性,您可以使用該屬性指定占位符以占用 <video> 元素的空間,直到延遲加載視頻為止。 與上文中的 <img>延遲加載示例一樣,我們將視頻網址存放在每個 <source> 元素的 data-src 屬性中。 然后,我們將使用與上文基于 Intersection Observer 的圖像延遲加載示例類似的 JavaScript:

          document.addEventListener("DOMContentLoaded", function() {
           var lazyVideos=[].slice.call(document.querySelectorAll("video.lazy"));
           if ("IntersectionObserver" in window) {
           var lazyVideoObserver=new IntersectionObserver(function(entries, observer) {
           entries.forEach(function(video) {
           if (video.isIntersecting) {
           for (var source in video.target.children) {
           var videoSource=video.target.children[source];
           if (typeof videoSource.tagName==="string" && videoSource.tagName==="SOURCE") {
           videoSource.src=videoSource.dataset.src;
           }
           }
           video.target.load();
           video.target.classList.remove("lazy");
           lazyVideoObserver.unobserve(video.target);
           }
           });
           });
           lazyVideos.forEach(function(lazyVideo) {
           lazyVideoObserver.observe(lazyVideo);
           });
           }
          });
          

          延遲加載 <video> 元素時,我們需要對所有的 <source> 子元素進行迭代,并將其 data-src 屬性更改為 src 屬性。 完成該操作后,必須通過調用該元素的 load 方法觸發視頻加載,然后該媒體就會根據 autoplay 屬性開始自動播放。

          利用這種方法,我們即可提供模擬動畫 GIF 行為的視頻解決方案。這種方案的流量消耗量低于動畫 GIF,而且能延遲加載內容。

          延遲加載庫

          如果您并不關心延遲加載的_實現_細節,只想直接選擇使用現有的庫(無需感到羞愧!),您有很多選項可以選擇。 許多庫使用與本指南所示標記模式相似的標記模式。 以下提供一些實用的延遲加載庫:

          • lazysizes 是功能全面的延遲加載庫,可以延遲加載圖像和 iframe。 其使用的模式與本文所示的代碼示例非常相似,會自動與 <img> 元素上的 lazyload 類綁定,并要求您在 data-src 和/或 data-srcset 屬性中指定圖像網址,這兩個屬性的內容將分別交換到 src 和/或 srcset 屬性中。 該庫使用 Intersection Observer(您可以使用 polyfill),并可以通過許多插件進行擴展,以執行延遲加載視頻等操作。
          • lozad.js 是超輕量級且只使用 Intersection Observer 的庫。 因此,它的性能極佳,但如果要在舊瀏覽器上使用,則需要 polyfill。
          • blazy 是另一個輕量級的延遲加載器(大小為 1.4 KB)。 與 lazysizes 相同,blazy 不需要任何第三方實用程序即可進行加載,并且適用于 IE7+。 但其缺點是不使用 Intersection Observer。
          • yall.js 是我編寫的庫,該庫使用 Intersection Observer,可回退到事件處理程序, 而且與 IE11 和主流瀏覽器兼容。
          • 如果您正在尋找 React 特定的延遲加載庫,您可考慮使用 react-lazyload。 雖然該庫不使用 Intersection Observer,但_的確_為習慣于使用 React 開發應用的開發者提供熟悉的圖像延遲加載方法。

          可能出錯的地方

          雖然延遲加載圖像和視頻會對性能產生重要的積極影響,但這項任務并不輕松。 如果出錯,可能會產生意想不到的后果。 因此,務必要牢記以下幾點:

          注意首屏

          使用 JavaScript 對頁面上的所有媒體資源進行延遲加載很誘人,但您必須抵擋住這種誘惑。 首屏上的任何內容皆不可進行延遲加載, 而應將此類資源視為關鍵資產,進行正常加載。

          以正常而非延遲加載方式加載關鍵媒體資源的主要理據是,延遲加載會將這些資源的加載延遲到 DOM 可交互之后,在腳本完成加載并開始執行時進行。 對于首屏線以下的圖像,可以采用延遲加載,但對于首屏上的關鍵資源,使用標準的 <img> 元素來加載速度會快得多。

          當然,如今用來查看網站的屏幕多種多樣,且大小各有不同,因此首屏線的具體位置并不明確。 筆記本電腦上位于首屏的內容在移動設備上可能位于首屏線_以下_。 目前并沒有完全可靠的建議,無法在每種情況下完美解決這個問題。 您需要清點頁面的關鍵資產,并以典型方式加載這些圖像。

          此外,您可能也不想嚴格限定首屏線作為觸發延遲加載的閾值。 對您來說,更理想的做法是在首屏線以下的某個位置建立緩沖區,以便在用戶將圖像滾動到視口之前,即開始加載圖像。 例如,Intersection Observer API 允許您在創建新的 IntersectionObserver 實例時,在 options 對象中指定 rootMargin 屬性。 如此即可為元素提供緩沖區,以便在元素進入視口之前觸發延遲加載行為:

          let lazyImageObserver=new IntersectionObserver(function(entries, observer) {
           // Lazy loading image code goes here
          }, {
           rootMargin:"0px 0px 256px 0px"
          });
          

          如果 rootMargin 的值與您為 CSS margin 屬性指定的值相似,這是因為該值就是如此!在本例中,我們將觀察元素(默認情況下為瀏覽器視口,但可以使用 root 屬性更改為特定的元素)的下邊距加寬 256 個像素。 這意味著,當圖像元素距離視口不超過 256 個像素時,回調函數將會執行,即圖像會在用戶實際看到它之前開始加載。

          要使用滾動事件處理代碼實現這種效果,只需調整 getBoundingClientRect 檢查以包括緩沖區,如此一來,您即可在不支持 Intersection Observer 的瀏覽器上獲得相同效果。

          布局移位與占位符

          若不使用占位符,延遲加載媒體可能會導致布局移位。 這種變化不僅會讓用戶產生疑惑,還會觸發成本高昂的 DOM 布局操作,進而耗用系統資源,造成卡頓。 您至少應考慮使用純色占位符來占用尺寸與目標圖像相同的空間,或者采用 LQIP 或 SQIP 等方法,在媒體項目加載前提供有關其內容的提示。

          對于 <img> 標記,src 最初應指向一個占位符,直到該屬性更新為最終圖像的網址為止。 請使用 <video> 元素中的 poster 屬性來指向占位符圖像。 此外,請在 <img> 和 <video> 標記上使用 width 和 height 屬性。 如此可以確保從占位符轉換為最終圖像時,不會在媒體加載期間改變該元素的渲染大小。

          圖像解碼延遲

          在 JavaScript 中加載大型圖像并將其放入 DOM 可能會占用主線程,進而導致解碼期間用戶界面出現短時間無響應的情況。 您可以先使用 decode 方法異步解碼圖像,再將其插入到 DOM 中,以減少此類卡頓現象,但請注意: 這種方法尚不能通用,而且會增加延遲加載邏輯的復雜性。 如果要采用這種方法,請務必進行檢查。 以下示例顯示如何通過回退來使用 Image.decode()

          var newImage=new Image();
          newImage.src="my-awesome-image.jpg";
          if ("decode" in newImage) {
           // Fancy decoding logic
           newImage.decode().then(function() {
           imageContainer.appendChild(newImage);
           });
          } else {
           // Regular image load
           imageContainer.appendChild(newImage);
          }
          

          請參閱此 CodePen 鏈接,查看與此示例相似的代碼的實際運行情況。 如果您大部分的圖像都相當小,則這種方法的幫助不大,但肯定有助于減少延遲加載大型圖像并將其插入 DOM 時的卡頓現象。

          內容不加載

          有時,媒體資源會因為某種原因而加載失敗,進而導致發生錯誤。 何時會發生這種情況?何時發生視情況而定,以下是一種假設情況: 您有一個短時間(例如,5 分鐘)的 HTML 緩存策略,而用戶訪問網站,_或_保持打開舊選項卡并長時間離開(例如,數個小時),然后返回繼續閱讀內容。 在此過程中的某個時刻,發生重新部署。 在此部署期間,圖像資源的名稱因為基于哈希的版本控制而更改,或者完全移除。 當用戶延遲加載圖像時,該資源已不可用,因此導致加載失敗。

          雖然出現這種情況的機會比較小,但您也有必要制定后備計劃,以防延遲加載失敗。 對于圖像,可采取如下解決方案:

          var newImage=new Image();
          newImage.src="my-awesome-image.jpg";
          newImage.onerror=function(){
           // Decide what to do on error
          };
          newImage.onload=function(){
           // Load the image
          };
          

          發生錯誤時采取何種措施取決于應用。 例如,可以將圖像占位符區域替換為按鈕,以允許用戶嘗試重新加載該圖像,或者直接在圖像占位符區域顯示錯誤消息。

          此外,也可能會發生其他情況。 無論采取何種方法,在發生錯誤時通知用戶,并提供可能的解決方案總不是壞事。

          JavaScript 可用性

          不應假定 JavaScript 始終可用。 如果要延遲加載圖像,請考慮提供 <noscript> 標記,以便在 JavaScript 不可用時顯示圖像。 例如,最簡單的回退方法是使用 <noscript> 元素在 JavaScript 處于關閉狀態時提供圖像:

          <!-- An image that eventually gets lazy loaded by JavaScript -->
          <img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load.jpg" alt="I'm an image!">
          <!-- An image that is shown if JavaScript is turned off -->
          <noscript>
           <img src="image-to-lazy-load.jpg" alt="I'm an image!">
          </noscript>
          

          如果 JavaScript 已關閉,用戶會_同時_看到占位符圖像以及 <noscript> 元素中包含的圖像。 要解決此問題,我們可以在 <html> 標記上放置 no-js 類,如下所示:

          <html class="no-js">
          

          然后,在通過 <link> 標記請求任何樣式表之前,于 <head> 中放置一行內聯腳本,用于在 JavaScript 處于打開狀態時從 <html> 元素中移除 no-js 類:

          <script>document.documentElement.classList.remove("no-js");</script>
          

          最后,我們可以使用一些 CSS,在 JavaScript 不可用時隱藏類為 lazy 的元素,如下所示:

          .no-js .lazy {
           display: none;
          }
          

          這并不會阻止占位符圖像加載,但是結果卻更令人滿意。 關閉 JavaScript 的用戶不只是能看到占位符圖像,這要比只能看到占位符和沒有意義的圖像內容更好。


          結論

          務必謹慎使用延遲加載圖像和視頻方法,該方法可以顯著減少網站上的初始加載時間和頁面負載。 用戶不查看的媒體資源不會為其帶來不必要的網絡活動和處理成本,但用戶可以根據需要查看這些資源。

          就性能改進方法而言,延遲加載無可爭議。 如果您的網站上存在大量內聯圖像,這是減少非必要下載的好方法。 您的網站用戶和項目干系人都會因該方法而受益匪淺!


          主站蜘蛛池模板: 一区二区三区无码高清| 成人区人妻精品一区二区不卡视频 | AV鲁丝一区鲁丝二区鲁丝三区| 国模一区二区三区| 东京热人妻无码一区二区av| 好吊妞视频一区二区| 无码少妇精品一区二区免费动态| 无码人妻精品一区二区三区久久久 | 亚欧色一区W666天堂| 视频在线观看一区| 亲子乱av一区区三区40岁| 色久综合网精品一区二区| 久久亚洲色一区二区三区| 日本视频一区二区三区| 天堂一区二区三区精品| 国产精品视频一区二区三区| 久久99国产精品一区二区| 国产精品一区在线观看你懂的| 国产在线观看91精品一区| 人妻少妇精品视频三区二区一区| 男女久久久国产一区二区三区| 亚洲国产精品一区第二页| 国偷自产Av一区二区三区吞精 | 国产成人一区二区三区在线| 日本一区视频在线播放| 国产午夜一区二区在线观看| 国产成人精品一区二区A片带套 | 日韩一区二区视频在线观看 | 蜜臀AV一区二区| 夜色福利一区二区三区| 亚洲不卡av不卡一区二区| 国产免费无码一区二区| 国产av熟女一区二区三区| 亚洲AV无码一区二区三区系列| 夜夜添无码试看一区二区三区| 中文字幕人妻无码一区二区三区| 中文字幕一区在线播放| 日韩AV无码一区二区三区不卡毛片 | 日韩色视频一区二区三区亚洲| 国产精品无码一区二区三区不卡| 毛片一区二区三区无码|