懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好优化网页性能的方式。用户滚动到它们之前,可视区域外的图像不会加载。这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。在某些情况下,它还可以帮助减少服务器负载。
适用场景:商城网站或者图片很多的其他展示性网站中。
那使用懒加载的优点有哪些呢?
- 能提升用户的体验。
- 减少无效资源的加载,减小服务器和浏览器的负担。
- 防止并发加载的资源过多而导致阻塞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));
公众号:Coder 杂谈,欢迎关注