「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。
图片懒加载是很常用的页面优化方式,在一定程度上节省了服务器的开销,有非常多已经成熟的库可以使用,当知道了原理以后实现起来难度也不是很大,今天就来手动的实现一下
原理与实现思路
- 当页面上有较多的图片需要展示,不用一次性加载所用图片,而是当图片
出现在可视范围内再加载 - 加载图片的过程:一般将图片
url放在img标签的src属性上,页面加载到img就会立即请求图片的url,当请求成功后,就会把真实的图片渲染到页面上,请求是一个异步的过程,这和网络、浏览器的性能都有关,难免会造成白屏的情况 - 因此一开始可以不设置
img标签的src属性,先用其他的标签存放url,在需要加载的时候把url放到src属性上
// 初始化不会加载图片
<img data-src="xxx.jpg">
// 替换后加载图片
<img src="xxx.jpg">
- 如何判定图片是否出现在可视范围内:假设浏览器可视窗口区域的高度
clientHeight,图片距离窗口顶部的高度rect.top,当rect.top < clientHeight时则说明图片出现在了可视范围内,此时就可以去请求图片的url地址
// 窗口高度
var clientHeight = window.innerHeight
// 图片距离窗口顶部的高度
var rect = img.getBoundingClientRect()
if(rect.top < clientHeight) {
// 这里去加载图片
}
代码实现
- 初始化造一些数据
<style>
.content {
width: 640px;
margin: 0 auto;
}
.content img {
width: 640px;
height: 360px;
margin-bottom: 10px;
background: gray; // 默认给一个背景色
}
</style>
<div class="content">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=1">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=2">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=3">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=4">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=5">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=6">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=7">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=8">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=9">
<img data-src="https://img9.51tietu.net/pic/2019-090921/lo24s1wwniplo24s1wwnip.jpg?v=10">
</div>
监听scroll事件实现
function lazyLoad() {
// 窗口高度
var clientHeight = window.innerHeight
var imgList = document.querySelectorAll('img[data-src]')
for (var i = 0; i < imgList.length; i++) {
var img = imgList[i]
var rect = img.getBoundingClientRect()
// 当前图片距离顶部小于窗口高度则加载
if(rect.top < clientHeight - 100) {
loadImg(img)
}
}
}
// 加载图片
function loadImg(el) {
var src = el.getAttribute('data-src')
var img = new Image()
img.src = src
img.onload = function () {
el.src = src
// 删除临时属性,下次就不会再遍历到
el.removeAttribute('data-src')
}
}
// 初始化没有滚动先执行一次
lazyLoad()
// 监听scroll,每滚动一次触发lazyload
document.addEventListener('scroll', lazyLoad)
-
效果图
-
缺点:scroll触发非常频繁,每触发一次都会去执行一次lazyload函数,在性能上存在一定的浪费
IntersectionObserver API 实现
还不知道什么是IntersectionObserver?阮一峰 IntersectionObserver API 使用教程
const observer = new IntersectionObserver(changes => {
const clientHeight = window.innerHeight
changes.forEach(item => {
const { target, isIntersecting } = item
if (isIntersecting) { //到达窗口可视区域
loadImg(target)
observer.unobserve(target) // 加载图片后解除观察
}
})
})
function loadImg(el) {
var src = el.getAttribute('data-src')
var img = new Image()
img.src = src
img.onload = function () {
el.src = src
el.removeAttribute('data-src')
}
}
document.querySelectorAll('img[data-src]').forEach(item => {
observer.observe(item)
})
- 优点:浏览器自动监听元素是否出现在可视范围内
- 缺点:未兼容所有浏览器