原生|原生 JS 实现最简单的图片懒加载

懒加载

什么是懒加载 懒加载其实就是延迟加载,是一种对网页性能优化的方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
什么时候用懒加载 当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
懒加载原理 我们都知道HTML中的 原生|原生 JS 实现最简单的图片懒加载
文章图片



原生|原生 JS 实现最简单的图片懒加载
文章图片



原生|原生 JS 实现最简单的图片懒加载
文章图片



原生|原生 JS 实现最简单的图片懒加载
文章图片



原生|原生 JS 实现最简单的图片懒加载
文章图片



仔细观察一下, 原生|原生 JS 实现最简单的图片懒加载
文章图片
可以看出返回的元素位置是相对于左上角而言的,而不是边距。
我们思考一下,什么情况下图片进入可视区域。
假设 constbound=el.getBoundingClientRect(); 来表示图片到可视区域顶部距离; 并设 constclientHeight=window.innerHeight; 来表示可视区域的高度。
随着滚动条的向下滚动, bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当 bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。
我们这样判断:
function isInSight(el) {

const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
//如果只考虑向下滚动加载
//const clientWidth = window.innerWeight;
return bound.top <= clientHeight + 100;
}
这里有个+100是为了提前加载。
加载图片 页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。
function checkImgs() {

const imgs = document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el => {
if (isInSight(el)) {
loadImg(el);
}
})
}
【原生|原生 JS 实现最简单的图片懒加载】function loadImg(el) {
if (!el.src) {
const source = el.dataset.src;
el.src = https://www.it610.com/article/source;
}
}
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
函数节流 在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
获取第一次触发事件的时间戳
获取第二次触发事件的时间戳
时间差如果大于某个阈值就执行事件,然后重置第一个时间
function throttle(fn, mustRun = 500) {

const timer = null;
let previous = null;
return function() {
const now = new Date();
const context = this;
const args = arguments;
if (!previous){
previous = now;
}
const remaining = now - previous;
if (mustRun && remaining >= mustRun) {
fn.apply(context, args);
previous = now;
}
}
}
这里的 mustRun就是调用函数的时间间隔,无论多么频繁的调用 fn,只有 remaining>=mustRunfn才能被执行。
实验
页面打开时 原生|原生 JS 实现最简单的图片懒加载
文章图片
可以看出此时仅仅是加载了img1和img2,其它的img都没发送请求,看看此时的浏览器
原生|原生 JS 实现最简单的图片懒加载
文章图片
第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
页面滚动时 当我向下滚动,此时浏览器是这样
原生|原生 JS 实现最简单的图片懒加载
文章图片
此时第二张图片完全显示了,而第三张图片显示了一点点,这时候我们看看请求情况
原生|原生 JS 实现最简单的图片懒加载
文章图片
img3的请求发出来,而后面的请求还是没发出~
全部载入时 当滚动条滚到最底下时,全部请求都应该是发出的,如图
原生|原生 JS 实现最简单的图片懒加载
文章图片
更新
方法三 IntersectionObserver
经大佬提醒,发现了这个方法
先附上链接:
jjc大大:
https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver可以自动观察元素是否在视口内。
var io = new IntersectionObserver(callback, option);

// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
callback的参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:
属性描述time可见性发生变化的时间,单位为毫秒rootBounds与getBoundingClientRect()方法的返回值一样boundingClientRect目标元素的矩形区域的信息intersectionRect目标元素与视口(或根元素)的交叉区域的信息intersectionRatio目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0target被观察的目标元素,是一个 DOM 节点对象
我们需要用到 intersectionRatio来判断是否在可视区域内,当 intersectionRatio>0&&intersectionRatio<=1即在可视区域内。
代码
function checkImgs() {

const imgs = Array.from(document.querySelectorAll(".my-photo"));
imgs.forEach(item => io.observe(item));
}
function loadImg(el) {
if (!el.src) {
const source = el.dataset.src;
el.src = https://www.it610.com/article/source;
}
}
const io = new IntersectionObserver(ioes => {
ioes.forEach(ioe => {
const el = ioe.target;
const intersectionRatio = ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <= 1) {
loadImg(el);
}
el.onload = el.onerror = () => io.unobserve(el);
});
});


感兴趣的小伙伴,可以关注公众号【grain先森】,回复关键词 “vue”,获取更多资料,更多关键词玩法期待你的探索~

    推荐阅读