延遲加載(Lazyload)三種實現方式

定義:延遲加載也稱爲惰性加載,即在長網頁中延遲加載圖像。用戶滾動到它們以前,視口外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載將使網頁加載更快。在某些狀況下,它還能夠幫助減小服務器負載。
舉個例子來講明,當打開淘寶首頁的時候,只有在瀏覽器窗口裏的圖片纔會被加載,當你滾動首頁向下滑的時候,進入視口內的圖片纔會被加載,而其它從未進入視口的圖像不會也不會加載。css

那麼延遲加載有什麼好處:html

一、首先它能提高用戶的體驗,試想一下,若是打開頁面的時候就將頁面上全部的圖片所有獲取加載,若是圖片數量較大,對於用戶來講簡直就是災難,會出現卡頓現象,影響用戶體驗。
二、有選擇性地請求圖片,這樣能明顯減小了服務器的壓力和流量,也可以減少瀏覽器的負擔。
那麼下面就介紹延遲加載的三種實現方式:api

第一種

首先將頁面上的圖片的 src 屬性設爲 loading.gif,而圖片的真實路徑則設置在 data-src 屬性中,頁面滾動的時候計算圖片的位置與滾動的位置,當圖片出如今瀏覽器視口內時,將圖片的 src 屬性設置爲 data-src 的值,這樣,就能夠實現延遲加載。數組

下面是具體的實現代碼:瀏覽器

<!DOCTYPE html>
<html lang= "en" >
<head>
     <meta charset= "UTF-8" >
     <title>Lazyload 1</title>
     <style>
         img {
         display: block;
         margin-bottom: 50px;
         height: 200px;
     }
     </style>
</head>
<body>
     <img src= "images/loading.gif" data-src= "images/1.png" >
     <img src= "images/loading.gif" data-src= "images/2.png" >
     <img src= "images/loading.gif" data-src= "images/3.png" >
     <img src= "images/loading.gif" data-src= "images/4.png" >
     <img src= "images/loading.gif" data-src= "images/5.png" >
     <img src= "images/loading.gif" data-src= "images/6.png" >
     <img src= "images/loading.gif" data-src= "images/7.png" >
     <img src= "images/loading.gif" data-src= "images/8.png" >
     <img src= "images/loading.gif" data-src= "images/9.png" >
     <img src= "images/loading.gif" data-src= "images/10.png" >
     <img src= "images/loading.gif" data-src= "images/11.png" >
     <img src= "images/loading.gif" data-src= "images/12.png" >
     <script>
         function lazyload() {
         var images = document.getElementsByTagName( 'img' );
         var len    = images.length;
         var n      = 0;      //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷      
         return function () {
         var seeHeight = document.documentElement.clientHeight;
         var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
         for ( var i = n; i < len; i++) {
             if (images[i].offsetTop < seeHeight + scrollTop) {
                 if (images[i].getAttribute( 'src' ) === 'images/loading.gif' ) {
                  images[i].src = images[i].getAttribute( 'data-src' );
             }
             n = n + 1;
              }
         }
         }
     }
     var loadImages = lazyload();
     loadImages();          //初始化首頁的頁面圖片
     window.addEventListener( 'scroll' , loadImages, false );
     </script>
</body>
</html>

比較 image 的 offsetTop 與 seeHeight + scrollTop 的大小,當小於時則說明圖片已經出現過在視口中,這時候繼續判斷圖片是否已經替換過,若是沒有替換過,則進行替換。服務器

實現的效果:不斷滑動頁面時,圖片延遲加載markdown

你能夠拷貝個人代碼去進行實驗,可是請確保 HTML 同目錄下有 images 目錄而且含有 1~12.png 和 loading.gif。閉包

須要說起的是變量 n 是用來保存已經加載的圖片數量,避免每次都從第一張圖片開始遍歷,提高性能。上面的代碼用到了 JS 閉包的知識,若是你不太熟悉的話,能夠自行百度一下。函數

第二種

上面的代碼是沒什麼問題,可是性能誤差。若是直接將函數綁定在 scroll 事件上,當頁面滾動時,函數會被高頻觸發,這很是影響瀏覽器的性能。我粗略地估計一下,當簡單地滾動一下頁面,函數至少觸發了十來次,這顯然是十分不必的。
因此在作事件綁定的時候,能夠對 lazyload 函數進行函數節流(throttle)與函數去抖(debounce)處理。
這裏我並再也不另外介紹這兩種方案,若是你想了解的話能夠閱讀:JS魔法堂:函數節流(throttle)與函數去抖(debounce) – ^_^肥仔John – 博客園
簡單說來:性能

  • Debounce:一部電梯停在某一個樓層,當有一我的進來後,20秒後自動關門,這20秒的等待期間,又一我的按了電梯進來,這20秒又從新計算,直到電梯關門那一刻纔算是響應了事件。
  • Throttle:比如一臺自動的飲料機,按拿鐵按鈕,在出飲料的過程當中,無論按多少這個按鈕,都不會連續出飲料,中間按鈕的響應會被忽略,必需要等這一杯的容量所有出完以後,再按拿鐵按鈕纔會出下一杯。

下面就是通過 throttle 處理後的代碼:

<!DOCTYPE html>
<html lang= "en" >
<head>
     <meta charset= "UTF-8" >
     <title>Lazyload 2</title>
     <style>
     img {
         display: block;
         margin-bottom: 50px;
         height: 200px;
     }
     </style>
</head>
<body>
     <img src= "images/loading.gif" data-src= "images/1.png" >
     <img src= "images/loading.gif" data-src= "images/2.png" >
     <img src= "images/loading.gif" data-src= "images/3.png" >
     <img src= "images/loading.gif" data-src= "images/4.png" >
     <img src= "images/loading.gif" data-src= "images/5.png" >
     <img src= "images/loading.gif" data-src= "images/6.png" >
     <img src= "images/loading.gif" data-src= "images/7.png" >
     <img src= "images/loading.gif" data-src= "images/8.png" >
     <img src= "images/loading.gif" data-src= "images/9.png" >
     <img src= "images/loading.gif" data-src= "images/10.png" >
     <img src= "images/loading.gif" data-src= "images/11.png" >
     <img src= "images/loading.gif" data-src= "images/12.png" >
     <script>
     function throttle(fn, delay, atleast) {
         var timeout = null ,
         startTime = new Date();
         return function () {
         var curTime = new Date();
         clearTimeout(timeout);
         if (curTime - startTime >= atleast) {
             fn();
             startTime = curTime;
         } else {
             timeout = setTimeout(fn, delay);
         }
         }
     }
     function lazyload() {
         var images = document.getElementsByTagName( 'img' );
         var len    = images.length;
         var n      = 0;      //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷      
         return function () {
         var seeHeight = document.documentElement.clientHeight;
         var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
         for ( var i = n; i < len; i++) {
             if (images[i].offsetTop < seeHeight + scrollTop) {
                 if (images[i].getAttribute( 'src' ) === 'images/loading.gif' ) {
                  images[i].src = images[i].getAttribute( 'data-src' );
                 }
             n = n + 1;
              }
         }
         }
     }
     var loadImages = lazyload();
     loadImages();          //初始化首頁的頁面圖片
     window.addEventListener( 'scroll' , throttle(loadImages, 500, 1000), false );
     </script>
</body>
</html>

設置了 500ms 的延遲,和 1000ms 的間隔,當超過 1000ms 未觸發該函數,則當即執行該函數,否則則延遲 500ms 執行該函數。

實現效果:能夠看出有必定的延遲。

參考連接:實現圖片懶加載(lazyload)

第三種: 使用 IntersectionObserver API

目前有一個新的 IntersectionObserver API,能夠自動」觀察」元素是否可見,Chrome 51+ 已經支持。

這裏不過多介紹 IntersectionObserver API 的詳細使用,感興趣能夠另外閱讀下面的文章:

IntersectionObserver API 使用教程
Intersection Observer API

實現代碼:簡潔,可是瀏覽器還沒有所有實現。

<!DOCTYPE html>
<html lang= "en" >
<head>
     <meta charset= "UTF-8" >
     <title>Lazyload 3</title>
     <style>
         img {
         display: block;
         margin-bottom: 50px;
         width: 800px;
         }
     </style>
</head>
<body>
     <img src= "images/loading.gif" data-src= "images/1.png" >
     <img src= "images/loading.gif" data-src= "images/2.png" >
     <img src= "images/loading.gif" data-src= "images/3.png" >
     <img src= "images/loading.gif" data-src= "images/4.png" >
     <img src= "images/loading.gif" data-src= "images/5.png" >
     <img src= "images/loading.gif" data-src= "images/6.png" >
     <img src= "images/loading.gif" data-src= "images/7.png" >
     <img src= "images/loading.gif" data-src= "images/8.png" >
     <img src= "images/loading.gif" data-src= "images/9.png" >
     <img src= "images/loading.gif" data-src= "images/10.png" >
     <img src= "images/loading.gif" data-src= "images/11.png" >
     <img src= "images/loading.gif" data-src= "images/12.png" >
     <script>
     function query(selector) {
         return Array.from(document.querySelectorAll(selector));
     }
     var io = new IntersectionObserver( function (items) {
         items.forEach( function (item) {
         var target = item.target;
         if (target.getAttribute( 'src' ) == 'images/loading.gif' ) {
             target.src = target.getAttribute( 'data-src' );
         }
         })
     });
     query( 'img' ).forEach( function (item) {
         io.observe(item);
     });
     </script>
</body>
</html>

一、IntersectionObserver 傳入一個回調函數,當其觀察到元素集合出現時候,則會執行該函數。
二、io.observe 即要觀察的元素,要一個個添加才能夠。
三、io 管理的是一個數組,當元素出現或消失的時候,數組添加或刪除該元素,而且執行該回調函數。
實現效果:

原文地址:https://zhuanlan.zhihu.com/p/25455672

相關文章
相關標籤/搜索