什么是图片懒加载
进入页面的时候,只请求可视区域的图片资源
懒加载原理
图片的标签是 img 标签,图片的来源主要是 src 属性,浏览器是否发起加载图片的请求是根据是否有src属性决定的。
所以可以从 img 标签的 src 属性入手,在没进到可视区域的时候,就先不给 img 标签的 src 属性赋值。
懒加载实现
- 监听 scroll 事件判断元素是否进入视口
const imgs = [...document.getElementsByTagName('img')];
let n = 0;
lazyload();
function throttle(fn, wait) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
fn.apply(this, args)
}, wait)
}
}
}
function lazyload() {
var innerHeight = window.innerHeight;
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for (let i = n; i < imgs.length; i++) {
if (imgs[i].offsetTop < innerHeight + scrollTop) {
imgs[i].src = imgs[i].getAttribute("data-src");
n = i + 1;
}
}
}
window.addEventListener('scroll', throttle(lazyload, 200));
存在问题:
- 每次滑动都要执行一次循环,如果有1000多个图片,性能会很差
- 每次读取 scrollTop 都会引起回流
- scrollTop 跟 DOM 的嵌套关系有关,应该根据 getboundingclientrect 获取(getBoundingClientRect 用于获取某个元素相对于视窗的位置集合,集合中有top, right, bottom, left 等属性)
- 滑到最后的时候刷新,会看到所有的图片都加载了
tips:
当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color,称为重绘。
注:回流必将引起重绘,而重绘不一定会引起回流。
-
IntersectionObserver
IntersectionObserver
接口提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport,可视视口)被称为根(root)。当一个
IntersectionObserver
对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
const imgs = [ ...document.getElementsByTagName('img') ];
// 当监听的元素进入可视范围内的会触发回调
if (IntersectionObserver) {
// 创建一个 intersection observer
const lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
let lazyImage = entry.target;
// 相交率,默认是相对于浏览器视窗
if (entry.intersectionRatio > 0) {
lazyImage.src = lazyImage.getAttribute('data-src');
// 当前图片加载完之后需要去掉监听
lazyImageObserver.unobserve(lazyImage);
}
})
})
for (let i = 0; i < imgs.length; i++) {
lazyImageObserver.observe(imgs[i]);
}
}
- vue自定义指令-懒加载
<img v-lazyload="image">
Vue.directive('lazyload', {
// 指令的定义
bind: function(el, binding) {
let lazyImageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
let lazyImage = entry.target;
// 相交率,默认是相对于浏览器视窗
if(entry.intersectionRatio > 0) {
lazyImage.src = binding.value;
// 当前图片加载完之后需要去掉监听
lazyImageObserver.unobserve(lazyImage);
}
})
})
lazyImageObserver.observe(el);
},
});
网友评论