前言
图片懒加载是前端性能提升的重要方面之一,其主要的用途是: 减少网络加载,提升体验。
但为了影响体验,你需要最好写好布局样式,最好写好占位图。
懒加载的原理
其实图片的加载主要是因为图片的src引起的,而如果我们想要实现懒加载,就避免src路径的加载即可,我们可以将图片的路径存储到data-src的自定义数据属性上,然后在需要的时候按需加载。
按需加载的实现方式有两种:这里重点说第一种,目前在移动端以及在pc的瀑布流布局中都是第一种居多。
1 滚动到页面底部加载
2 分页加载
滚动到页面底部加载的原理
原来的方式:滚动监听
其主要实现的原理是对滚动事件的监听,当发现滚动条高度+滚动条高度等于document的高度时,就是达到页面的底部了,需要重新加载图片的时机。
$(window).scroll(function(){
if($(window).scrollTop() +$(window).height()>=$(document).height()){
var $div = '<li>append添加的节点</li>';
$('#container').append($div)
}
});
新的方式:IntersectionObserver
我们去看下jq的lazyload的源码是如何写这部分函数的,核心是依赖IntersectionObserver,是检测元素是否进入视图,如果有进入,那么就会移除其lazy的class,其源码地址:链接
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
}
});
如何实现图片懒加载
概念
图片懒加载是针对图片加载时机的一种优化,在一些图片量比较大的网站(比如电商网站首页,或者团购网站、小游戏首页等),如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象,因为图片真的太多了,一口气处理这么多任务,浏览器做不到啊!
懒加载是为了让浏览器只加载可视区内的图片,可视区外的大量图片不进行加载,当页面滚动到后面去的时候再进行加载。这样做有很多好处可以增加首屏加载的速度,毕竟,用户点开页面的瞬间,呈现给他的只是首屏,我们只要把首屏的资源图片加载处理就可以了,至于下面的图片,当用户下滑当当前位置的时候,在加载出来也是没问题的,对于性能压力也小了,用户体验也没有变差。
解答
图片懒加载的原理就是需要知道图片是否在可视区内了,当图片到达可视区内就需要去请求对应的图片加载出来
页面中的img标签一般如下写
<img class="lazyload" src="placeholder.jpg" data-src="real_image.jpg" />
其中src首先赋值一个占位的图片,一般是一个很小的图片,进行占位,data-src是实际需要展示的图片,原理就是当图片在可视区内的时候将data-src的图片选渲染出来即可。
实现
1、原生实现
Chrome 76 将原生支持图片的惰性加载,支持对img和iframe进行延迟加载,只需要将loading属性设置为lazy即可。
<img src="celebration.jpg" loading="lazy" alt="..." /> <iframe src="video-player.html" loading="lazy"></iframe>
原生实现的好处是,不需要任何脚本,纯原生HTML即可,简单方便,支持多种属性
- lazy:对资源进行延迟加载。
- eager:立即加载资源。
- auto:浏览器自行判断决定是否延迟加载资源。
原生的坏处就是在于浏览器的支持率不是很高,将来肯定是非常好的。
2、Element.getBoundingClientRect()
getBoundingClientRect返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。DOMRect 对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。有了这个API后我们很同意获取图片的top值,当top值小于可视区的高度的时候就可以任何图片进入了可视区,直接加载图片即可
element.getBoundingClientRect().top < document.documentElement.clientHeight
由于需要在滚动的时候去监听图片的位置,所以我们需要使用到window.onscroll事件,我们在事件内部处理相关的逻辑即可。
3、通过相对计算获取元素位置
- 通过document.documentElement.clientHeight获取屏幕可视窗口高度。
- 通过element.offsetTop获取元素相对于文档顶部的距离。
- 通过document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离。 然后判断2-3<1是否成立,如果成立,元素就在可视区域内。
element.offsetTop - document.documentElement.scrollTop < document.documentElement.clientHeight
此方法也需要在滚动的时候去监听图片的位置,所以我们需要使用到window.onscroll事件,我们在事件内部处理相关的逻辑即可。
4、使用IntersectionObserver
const observer = new IntersectionObserver(callback, observerConfig)
const imgList = document.querySelectorAll(".lazyload");
const observer = new IntersectionObserver(entries => {
entries.forEach(item => {
if (item.isIntersecting) {
item.target.src = item.target.dataset.origin; // 判断在可视区了,把data-origin的值放到src
observer.unobserve(item.target); // 已经加载过的图片停止进行监听
}
});
});
imgList.forEach(item => observer.observe(item));
补充
1、优化
由于上面某些情况是需要使用到window.scroll事件的,所以我们可以增加节流来减少事件处理函数的调用次数。 假设我们判断是否可视区逻辑为函数loadImage那么我们可以如下处理。
window.onscroll = throttle(loadImage, 500)
2、拓展
上面后续三种方法不仅仅可以使用在图片的懒加载上面,其实所有可以懒加载的地方都可以通过这种方式进行判断,比如列表分页加载,我们可以通过这种方式进行判断是否需要进行下一页的加载,比如我们需要埋曝光埋点的时候,可以通过这种方法判断元素是否曝光,进行埋点事件的触发。
推荐文章
vue3-lazyload使用
"vue3-lazyload": "^0.3.6"
import VueLazyload from 'vue3-lazyload'
app.use(VueLazyload, {
loading: 'loading/loading.png',
error: 'loading/default.jpg'
})
// 页面使用
<img v-lazy="" />