最近在做一个图片list预览组件,在一组图片list 中点击其中一张图片 可以放大 缩小,旋转、下载等功能。 但是list 中可能很多图片,如果首次打开这个页面, 每个图片都加载,浪费服务器资源,也给页面渲染带来极大的性能损耗。(http请求是宝贵的);
一.如何做到懒加载 也叫按需加载?
懒加载原理 ,初始化时候给页面的img 标签的src属性 ,默认赋值loading图片,增加一个 自定义的data-src 属性 ,存放真实的图片地址,遍历判断img 标签在视口中 , 把data-src 中的真实地址,赋值给src属性 ,加载真实的图片。
二.如何判断img 标签已经在视口
原始的转化方式 :
1.
document.documentElement.clientHeight
获取屏幕可视窗口高度 ,
2.el.offsetTop 获取元素距离文档顶部的 距离,
3.document.documentElement.scrollTop 文档向下滚动的距离。 如下图 当 ② - ① < ③ 说明图片已经在视口内部。可以加载。
现成的API getBoundingClientRect :
浏览器提供 Element.getBoundingClientRect() 方法来获取元素的大小以及位置,包括 矩形 盒子的大小,top 、left、bottom right 。
如下图。这些位置信息是相对视口 左上角为原点返回的数值。什么情况下是img 进入视口的呢? 假设,我们获取img 相对视口的信息 const coordinateData= el.getBoundingClientRect() , const clientHeight = window.innerHeight , 很容易 coordinateData.top ===clientHeight 的 时候 ,img 将要进入视口。ok
function isInViewport(el){
try{
const {top,left,bottom,right}= el.getBoundingClientRect();
const height= window.innerHeight;
const width = window.innerWidth;
const yInView = top<height&&bottom>0;
const xInView = left<width&&right>0;
return yInView&&xInView;
}catch(err){}
}
function getImgs(){
const imgs= document.querySelectorAll('.photolist') ;
Array.from(imgs).forEach(item=>{
if (isInViewport(item)){
if (item['data-isLoaded'])return;
item.src = item['data-src'];
item['data-isLoaded'] = true;
}
})
}
上面的 代码 还为已经加载的img 标签添加了自定义属性, 如果该标签已经加载过图片, 以后的遍历就不会重新加载。
这里还可以优化下, 在页面滚动的时候 ,一直去访问dom 执行操作,对页面性能是很大的损耗。 这里我们可以写一个节流函数,
具体解释看这里;
function throttle(fn,delay){
let deferTimer , last;
return function (args){
const context = this;
const now = new Date();
const _args = arguments;
if(last&&now<last + delay){
if(!deferTimer){
deferTimer = setTimeout(()=>{
fn.apply(context,_args);
last=now;
clearTimeout(deferTimer)
},last+delay-now);
}
} else {
fn.apply(context,_args);
last=now;
}
}
}
window.addEventListener('scroll',()=>{
throttle(getImgs(),500ms)
});