lazyLoad-懶加載

懶加載也叫延遲加載,指的是在長網頁中延遲加載圖像,是一種很好優化網頁性能的方式。用戶滾動到它們之前,可視區域外的圖像不會加載。這與圖像預加載相反,在長網頁上使用延遲加載將使網頁加載更快。在某些情況下,它還可以幫助減少服務器負載。

適用場景:商城網站或者圖片很多的其他展示性網站中。

那使用懶加載的優點有哪些呢?

  • 能提升用戶的體驗。
  • 減少無效資源的加載,減小服務器和瀏覽器的負擔。
  • 防止併發加載的資源過多而導致阻塞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>
封裝版本
  1. 在上述代碼塊基礎上進行封裝–有優化空間

使用此方法,會在調用時,多次獲取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
  1. 使用類和單例模式進行封裝

此方法,將高度,選擇器,加載屬性都定義在了構造方法裏,並且使用單例模式,確保只初始化一個實例,之後的 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));
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章