懶加載也叫延遲加載,指的是在長網頁中延遲加載圖像,是一種很好優化網頁性能的方式。用戶滾動到它們之前,可視區域外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載將使網頁加載更快。在某些情況下,它還可以幫助減少服務器負載。
適用場景:商城網站或者圖片很多的其他展示性網站中。
那使用懶加載的優點有哪些呢?
- 能提升用戶的體驗。
- 減少無效資源的加載,減小服務器和瀏覽器的負擔。
- 防止併發加載的資源過多而導致阻塞JS的加載。
原理
那麼我們會經常把src的屬性設置爲空字符串,或者一個其他的靜態圖片,而圖片的真實路徑則設置在自定義屬性中去,比如 data-src。然後監聽頁面的滾動事件,在滾動事件的回調函數中,判斷我們的懶加載的圖片是否進入可視區域。如果進入到可視區域,就把src的值設置爲 data-src的值,從而實現懶加載的效果。
示例代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>lazy</title>
<style type="text/css">
img {
display: block;
width: 104px;
height: 144px;
}
</style>
</head>
<body>
<div class="lazyObj">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
<img src="" data-src="https://user-gold-cdn.xitu.io/2017/12/13/1604f3080efffc24?imageView2/1/w/104/h/144/q/95/format/webp/interlace/1">
</div>
</body>
</html>
<script type="text/javascript">
// 獲取可視區域的高度
var viewHeight = document.documentElement.clientHeight;
function lazy() {
// 獲取所有的Img標籤
var lazyImg = document.getElementsByTagName('img');
// 這裏打印出來的lazyImg是一個obj,所以不能直接使用 forEach 方法
// console.log(typeof lazyImg)
// 循環數組,將Array的forEach方法用到 lazyImg 上去
// 簡寫 [].forEach.call(...)
Array.prototype.forEach.call(lazyImg, function(item, index) {
// 定義一個變量來用作判斷是否進到可視區域的範圍
let rect;
if (item.dataset.src === "" || item.dataset.src === undefined) return;
// 用於獲得頁面中某個元素的左,上,右,下分別相對於瀏覽器視窗的位置
rect = item.getBoundingClientRect();
// 判斷圖片位置是否進入到了可視區域
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src;
item.removeAttribute("data-src");
}
})
}
// 這裏也需要給scroll事件放一個函數節流來減少性能損耗
function throttle (fn, time) {
let flag = true;
return function () {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, time)
}
}
lazy();
window.addEventListener('scroll', throttle(lazy, 300));
</script>
封裝版本
- 在上述代碼塊基礎上進行封裝–有優化空間
使用此方法,會在調用時,多次獲取DOM節點,尤其是在 scroll 這種高頻觸發的事件中,會消耗瀏覽器性能,且在使用節流時,得再包裹一層中間層,否則無法傳遞參數
優化建議:
如果是固定的元素節點,可以把獲取DOM部分也設爲一個常量;但如果是動態加載,則還是需要動態去獲取DOM節點。
// 獲取可視區域的高度
var viewHeight = document.documentElement.clientHeight;
function lazy(selector, type) {
// 獲取所有的Img標籤
var lazyImg = document.querySelectorAll(selector);
// 這裏打印出來的lazyImg是一個obj,所以不能直接使用 forEach 方法
// console.log(typeof lazyImg)
// 循環數組,將Array的forEach方法用到 lazyImg 上去
Array.prototype.forEach.call(lazyImg, function(item, index) {
// 定義一個變量來用作判斷是否進到可視區域的範圍
let rect;
if (item.dataset.src === "" || item.dataset.src === undefined) return;
// 用於獲得頁面中某個元素的左,上,右,下分別相對於瀏覽器視窗的位置
rect = item.getBoundingClientRect();
// 判斷圖片位置是否進入到了可視區域
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src;
item.removeAttribute(type);
}
})
}
// 這裏也需要給scroll事件放一個函數節流來減少性能損耗
function throttle (fn, time) {
let flag = true;
return function () {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, time)
}
}
// 使用此種傳參方式,得再包一層
lazy('img[data-src]', 'data-src');
// 中間層
function lazyLoad () {
lazy('img[data-src]', 'data-src');
}
window.addEventListener('scroll', throttle(lazyLoad, 300));
// 測試耗時 43.331298828125ms
- 使用類和單例模式進行封裝
此方法,將高度,選擇器,加載屬性都定義在了構造方法裏,並且使用單例模式,確保只初始化一個實例,之後的 onscroll 事件裏,只需要調用 init 方法即可。
可優化的點:
將循環後已經加載出來的的元素節點刪除掉,可提高下次循環效率
class Lazy {
constructor (selector, type) {
this.selector = selector;
this.type = type;
// 獲取可視區域的高度
this.viewHeight = document.documentElement.clientHeight;
// 獲取所有的Img標籤
this.lazyImg = document.querySelectorAll(this.selector);
this.init();
}
init () {
// this 作用域
let _this = this;
// 循環數組,將Array的forEach方法用到 lazyImg 上去
Array.prototype.forEach.call(_this.lazyImg, function(item, index) {
// 定義一個變量來用作判斷是否進到可視區域的範圍
let rect;
if (item.dataset.src === "" || item.dataset.src === undefined) return;
// 用於獲得頁面中某個元素的左,上,右,下分別相對於瀏覽器視窗的位置
rect = item.getBoundingClientRect();
// 判斷圖片位置是否進入到了可視區域
if (rect.bottom >= 0 && rect.top < _this.viewHeight) {
item.src = item.dataset.src;
item.removeAttribute(_this.type);
}
})
}
}
// 使用單例模式
Lazy.getSelector = (function(){
let instance;
return function (selector, type) {
if (!instance) {
instance = new Lazy(selector, type);
}
return instance;
}
})()
// 這裏也需要給scroll事件放一個函數節流來減少性能損耗
function throttle (obj, fn, time) {
let flag = true;
return function () {
if (!flag) return;
flag = false;
setTimeout(() => {
// fn();
fn.apply(obj, arguments);
// 這裏不能使用this,它會將作用域指向當前觸發事件的對象上去
// fn.apply(this, arguments);
flag = true;
}, time)
}
}
let lazy = Lazy.getSelector('img[data-src]', 'data-src');
window.addEventListener('scroll', throttle(lazy, lazy.init, 300));