前端性能优化之 —— 图片延迟加载 (原理以及实现方式)
2017, Feb 01
前端开发的时候,有些列表页面可能会有很多图片需要加载。一次加载太多图片,会占用很大的带宽,影响网页的加载速度。
这时候我们想到一种方式,让用户浏览到什么地方,就加载该处的图片。
这里写了一个简单的例子,大家可以去体验一下,当然这里考虑到的是最简单的情况。 这里简单的讲解一下这个例子里面的源码。
DOM 结构
由一个父容器div#lazy-img
,里面是图片标签,父容器是可以滚动的,图片有固定高度。大家可以看到,容器内的img元素没有 src
属性,而有一个 data-src
属性。 这是不想让图片提前加载,所以把图片的链接储存到data-src
内。
div#lazy-img
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img(data-src="/assets/img/9c61bc16-ae02-4f06-a8aa-0501db51eadb.png")
img{
width:600px;
height:300px;
border: 2px solid #eee;
background: #ccc;
}
#lazy-img{
height: 400px;
background:#eee;
overflow: auto;
}
Javascript 代码
我们获取容器内所有有 data-src
属性的dom对象,遍历获取他们的相对高度,储存成 { height: imgDom}
格式。然后监听 容器的滚动事件,当滚动的时候,计算当前滚动区域显示的图片dom,并将改 图片dom 的 src
的内容改成我们存储在 data-src
的内容。
var imgContainer = document.getElementById('lazy-img')
var imgs = imgContainer.querySelectorAll('[data-src]')
var imgHeightDomMapping = {}
imgs.forEach(function (img) {
imgHeightDomMapping[img.offsetTop + img.clientHeight / 1.5] = img
}) // 获取所有 dom 对象相对容器的高度 并储存成 key: height, value: imgDom
imgContainer.addEventListener('scroll', function () {
showImage()
})
function showImage () {
var currentHeight = imgContainer.clientHeight + imgContainer.scrollTop // 滚动区域的高度
Object.keys(imgHeightDomMapping).forEach(function (imageHeight) {
if (currentHeight > imageHeight) { // 判断当前图片是否已经显示
//将图片dom的 `src` 改为 `data-src` 的内容
imgHeightDomMapping[imageHeight].src = imgHeightDomMapping[imageHeight].getAttribute('data-src')
}
})
}
showImage() // 主动调用一次 加载首页的
这里简单的实现了一个延迟加载,主要是告诉大家原理,可扩展的地方还很多。 例如提前加载 N 张图片,可以包装成一个通用的插件等等。只要大家了解到了原理,那么实现折些都很简单。 这篇文章也算是抛砖引玉,欢迎大家进行讨论。