文章目录
1,懒加载的概念
对于页面有很多静态资源的情况下(比如网商购物页面),为了节省用户流量和提高页面性能,可以在用户浏览到当前资源(当前窗口(可视区域)的大小)的时候,再对资源进行请求和加载。
2,懒加载实现的原理
- 1,使用img标签,scr属性初始为空
- 2,写一个自定义属性字段,该属性的值写成图片地址
- 3,当图片在可视区域的范围内的时候,将自定义属性的值作为src的值
代码示例:
<img
src=""
lazyload="true"
data-src="https://t7.baidu.com/itu=1732966997,2981886582&fm=193&f=GIF"
alt=""
class="image-item"
/>
- data-src是我们自己定义的属性字段,它的值为我们要的图片地址
- lazyload="true"是为了当图片加载出来后将图片不在需要懒加载了。
3,可视区域表示
原理:
使用:document.documentElement.clientHeight 可以获取到当前屏幕的高度。
过程:
当页面发生上滑的时候,可视区域的图片就会发生改变。
当鼠标滚轮滚动的时候,在可视区域的图片就可能出去可视区域了,不在的可能这时候就进来了可视区域。所以我们要写一个监听事件来进行监听。
当我们拿到所有的 img 时,利用循环去判断他们是否在可视区域内,在就加载出来,不在就暂时不加载。
需要满足的条件是图片的顶部在可视区域的高度里面,图片的底部也要在可视区域里面,也就是图片没有被划出去。
代码:
//获取可视区域的高度
var viewHeight = document.documentElement.clientHeight;
document.addEventListener("scroll", function() {
//获取到页面上所有的img
//判断某个是否进入可视区域
//如果进入,就把它自身的data-original的值取出来放到src
var arr = document.querySelectorAll("img[data-src][lazyload]");
arr.forEach((item) => {
let rect = item.getBoundingClientRect(); //用于一次性获取某个容器相对于浏览器上下左右的位置
if (rect.top < viewHeight && rect.bottom >= 0) {
item.src = item.dataset.src;
item.removeAttribute("data-src");
item.removeAttribute("lazyload");
}
});
});
获取区域的方法:
- offsetTop方法: 图片出现在视窗内的情况: offsetTop < clientHeight + scrollTop
- getBoundingClientRect方法: 图片出现在视窗内的情况: element.getBoundingClientRect().top < clientHeight
- H5的intersectionObserve方法: intersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0
4,代码实现方式
(1) 使用 getBoundingClientRect方法
// offsetTop
// el.offsetTop - document.documentElement.scrollTop <= viewPortHeight
function isInViewPortOfOne (el) {
// viewPortHeight 兼容所有浏览器写法
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
const offsetTop = el.offsetTop
const scrollTop = document.documentElement.scrollTop
const top = offsetTop - scrollTop
return top <= viewPortHeight
}
document.addEventListener('DomContentLoader', function(){
let lazyImages = [].slice.call(document.querySelectorAll('#imag.lazy'))
//限制函数频繁被调用
let active= false
const lazyLoad = function(){
if(active === false){
active = true
setTimout(()=>{
lazyImages.forEach((lazyImage)=>{
// 判断该图片是否在是视窗中
if((lazyImage.getBoundingClientReat().top <= window.innetHeight && lazyImage.getBoundingClientReat().bottom >= 0 ) && getComputedStyle(lazyImage).display !== 'none'){
// 将真实的图片进行赋值
lazyImage.src = lazyImage.dataset.src
// 加载完成之后,要为该图片移除lazy这个属性
lazyImage.classList.remove('lazy')
// 在lazyImage中将加载后的图片移除
lazyImages = lazyImages.filter((image)=>{
return image !== lazyImage
})
// 如果所有的待加载图片全都加载完成之后, 移除触发函数
if(lazyImage.length == 0){
document.removeEventListener('scroll', lazyLoad);
wimdow.removeEventListener('resize', lazyLoad)
}
}
});
active = false
},200)
}
}
document.addEventListener('scroll', lazyLoad)
window.addEventListener('resize', lazyLoad)
})
由于用户可以以随心所欲的滑动鼠标滚轮,从而导致scroll事件被触发。在此代码中,将延迟加载的处理过程置于一个异步定时器中,通过修改标志位active的方式来进行限流,其实就是加上了节流操作。
即便这样,也是有骑在的性能问题,因为重复的定时器调用的浪费的。
(2)使用Intersection Observer 方式
对于Intersection Observer 的API,可以通过它来检查目标元素的可见性。作用是:每当因页面滚动或者窗口尺寸发生大小的时候,使得目标元素与设备视窗或者其他元素产生交集的时候,就会触发通过Intersection Observer API配置的回调函数,在该回调函数中进行延迟加载的逻辑处理,会比传统的方式更加简洁而高效。
document.addEvemtListener('DOMContentLoaded', function(){
let lazyImages = [].slice.call(document.querySelectorAll('img.lazy'))
// 判断浏览器的兼容性
if('IntersectionObserver' in window && 'intersectionObserverEnter' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype){
//新建IntersectionObserver对象 并在其回调函数中实现加载逻辑
let lazyImageObserver = new IntersectionObserver(funtion(entries, observer){
entries.foreach((entry)=>{
// 判断图片是否在视窗内
if(entry.isIntersecting){
let lazyImage = entry.target
lazyImage.src = lazyImage.dataset.src
// 图片加载完成之后,取消监控重复加载
lazyImage.classList.remove('lazy')
lazyImageObserver.unobserver(lazyImage)
}
})
})
// 对每一个懒加载的图片进行监听
lazyImages.forEach(function(lazyImage){
lazyImageObservr.observer(lazyImage);
})
}
})
唯一存在的问题,就是兼容性问题,在使用的时候,可以和传统的图片懒加载方式结合起来。