懒加载:
懒加载:在需要的时候才加载图片,延迟加载甚至不加载图片。
作用: 延迟请求数或减少请求数,减轻了服务器压力,页面加载速度更快。
实现:
实现原理:创建一个自定义属性 data-src
存放真正需要显示的图片路径;当需要加载图片的时候,才获取到该图片的 data-src
的值赋给 src。
例如:有多张图片纵向排列显示,滚动到可视区域内才懒加载显示。
.lazyloadImg {
width: 100%;
// 图片懒加载必须要给图片设置高度。否则的话,图片加载完成之前,高度没有被撑开为 0,一加载进来所有图片都在可视区域内,将会全都加载出来
height: 200px;
}
<div>
<img class="lazyloadImg" data-src="https://img0.baidu.com/it/u=1345303087,1528317222&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666198800&t=7eb134b9d7567021d3fbef74c31cbb6a" />
<img class="lazyloadImg" data-src="https://img0.baidu.com/it/u=1088754973,1390499664&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666198800&t=7e000c45a31f9aed7a303b81bdbc6539" />
<img class="lazyloadImg" data-src="https://img0.baidu.com/it/u=3156137851,1307209439&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666198800&t=66987b2f9f9d9b11ae6737a0d5323aab" />
<img class="lazyloadImg" data-src="https://img0.baidu.com/it/u=4008146120,512111027&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666198800&t=7566843c36c23fbc7930c2fc9e9b2ed7" />
<img class="lazyloadImg" data-src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1666198800&t=54bb74703aba51ea4d514e9b011526f3" />
<div>
使用 getBoundingClientRect()
方法得到目标元素相对于视口左上角的坐标,再判断是否在视口之内。
// 图片懒加载的实现代码:
// DOM 元素是否在可视区域内
const windowHeight = window.innerHeight
const windowWidth = window.innerWidth
const isvisibleArea = el => {
const rect = el.getBoundingClientRect()
return rect.bottom > 0 && rect.top < windowHeight && rect.right > 0 && rect.left < windowWidth
}
const imgEls = [...document.querySelectorAll('.lazyloadImg')]
const lazyLoadImg = () => {
for (let i = 0; i < imgEls.length; i++) {
const imgEl = imgEls[i]
if (isvisibleArea(imgEl)) {
imgEl.src = imgEl.dataset.src
// 已经渲染过的就删除掉,避免重复请求
imgEls.splice(i, 1)
i--
}
}
}
lazyLoadImg()
// 此处可使用防抖来优化 lazyLoadImg 的执行
// 此处不可使用节流。因为如果使用节流的话,一触发滚动,lazyLoadImg 就会开始执行一次,但此时元素还没进入可视区域,导致无法渲染出来
window.addEventListener('scroll', lazyLoadImg)
使用交叉观察器 IntersectionObserver:
IntersectionObserver 是浏览器原生提供的构造函数,可以自动观察元素是否可见。接受两个参数:callback 是可见性变化时的回调函数,option 是可选的配置对象。返回值是一个观察器实例。
var io = new IntersectionObserver(callback, option)
目标元素的可见性 visibity 变化时,就会调用观察器的回调函数 callback。callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。callback 函数的参数 entries 是一个数组,每个成员都是一个IntersectionObserverEntry 对象,IntersectionObserverEntry 对象提供目标元素的信息,一共有六个属性。
var io = new IntersectionObserver(
entries => {
console.log(entries)
}
)
{
// IntersectionObserverEntry 对象的属性:
// 可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
time: 3893.92,
// 根元素的矩形区域的信息,getBoundingClientRect() 方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
// 目标元素的矩形区域的信息
boundingClientRect: ClientRect {
// ...
},
//目标元素与视口(或根元素)的交叉区域的信息
intersectionRect: ClientRect {
// ...
},
/ /目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1,完全不可见时小于等于 0
intersectionRatio: 0.54,
// 被观察的目标元素,是一个 DOM 节点对象
target: element
}
实例方法:
observe()
:开始观察,可以指定观察哪个 DOM 节点。参数是一个 DOM 节点对象。如果要观察多个节点,就要多次调用这个方法。unobserve()
:停止观察。disconnect()
:关闭观察器。
// 图片懒加载的实现代码:
var observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
var imgEl = entry.target
imgEl.src = imgEl.dataset.src
observer.unobserve(imgEl)
}
})
}
)
const lazyLoadImg = () => {
const imgEls = [...document.querySelectorAll('.lazyloadImg')]
imgEls.forEach((imgEl) => {
observer.observe(imgEl)
})
}
lazyLoadImg()
预加载:
预加载:提前加载图片,当用户需要查看时就可直接从本地缓存中读取。
如果图片在使用之前就已经请求过了,那么再次使用的时候,就不会再去请求了,而是直接从本地缓存中读取。
作用:能使用户的操作得到最快的反映,提升了用户体验。但是增加了服务器压力,首页加载时间变长。
实现:
CSS 实现:隐藏在 background 的 url 属性里面。
缺点:图片跟随文档一同加载,图片较多的时候会导致加载缓慢。
.preloadImg {
// 使用 background 属性加载图片,但是并不显示出来
background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px;
}
JS 实现:通过 JS 中的 Image 对象。
const preloadImg(imgUrls) {
imgUrls.forEach(imgUrl => {
const img = new Image()
img.src = imgUrl
})
}
var imgUrls = [
'./imgsrc1',
'./imgsrc2',
'./imgsrc3',
];
preloadImg(imgUrls)