时间:2022-05-03 10:57:21 | 栏目:JavaScript代码 | 点击:次
在我们访问一个图片展示比较多的网页时,加载速度慢很多时候正是因为图片多导致,大量的img图片导致页面渲染的堵塞。当费了许多力气把全部图片和页面加载出来时而用户早已离去。另一方面,若用户只查看了网页的前面部分便离开,许多已经加载却因为处于网页底部而未呈现在视口区的图片,它们极大加重服务器压力了但是用户看都没看,白白浪费了性能。
为了解决上面的问题需要引入图片懒加载,懒加载其实很好理解,重点就是一个‘懒'字。当用户滚动相应可视区域,若可视区域有图片便加载,而在可视区域外未加载过的图片它们先不加载,如果用户滚动可视区域到它们时它们再加载,否则一律不加载。这样一来就大大提高了网页渲染的性能和减少不必要的浪费。
首先,先定义一个基本的HTML页面模拟一些存在大量图片的网页,比如我用8个img 标签来模拟,同时定义一些基本css样式,代码与初始效果如下:
html:
<img src="img/1.jpg" alt="xxx" /> <img src="img/2.jpg" alt="xxx" /> <img src="img/3.jpg" alt="xxx" /> <img src="img/4.jpg" alt="xxx" /> <img src="img/5.jpg" alt="xxx" /> <img src="img/6.jpg" alt="xxx" /> <img src="img/7.jpg" alt="xxx" /> <img src="img/8.jpg" alt="xxx" />
css:
* { margin: 0; padding: 0; box-sizing: border-box; } img { width: 500px; height: 300px; object-fit: cover; margin: 20px; } body { display: flex; flex-wrap: wrap; justify-content: space-evenly; }
初始效果如下,可以看到右边的控制台,8张图片在我一运行这个页面的时候就都一同被加载渲染了:
下面是利用JavaScript实现懒加载的3种方式,原理都是判断图片是否出现在可视区后给图片赋值src属性。
首先,修改每一个img标签,利用HTML提供的 data- 属性来嵌入自定义数据,这个自定义数据我们存放这个标签原本的图片地址。同时,全部的图片的src属性我们都用一张同样的图表示,这个图一般可为显示载入中字样的图片。注意,如果多个img标签src引用的是同一张图片,那么只会加载一次,不会多次加载,所以我下面给每个图src定义同一张图。
<img data-src="img/1.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/2.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/3.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/4.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/5.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/6.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/7.jpg" src="img/0.png" alt="xxx" /> <img data-src="img/8.jpg" src="img/0.png" alt="xxx" />
此时页面效果如下,:
接下来用JavaScript实现当我们滚动滚动条时,如果图片出现在可视区,那么加载图片。加载图片其实就是给img标签src属性赋值为本来的地址,那么此时图片便会请求加载渲染出来。
//获取全部img标签 var images = document.getElementsByTagName("img"); window.addEventListener("scroll", (e) => { //当发生滚动事件时调用ergodic事件 ergodic(); }); function ergodic() { // 遍历每一张图 for (let i of images) { //判断当前图片是否在可视区内 if (i.offsetTop <= window.innerHeight + window.scrollY) { //获取自定义data-src属性的值 let trueSrc = i.getAttribute("data-src"); //把值赋值给图片的src属性 i.setAttribute("src", trueSrc); } } } //没发生滚动事件时也要先执行一次 ergodic();
其中, offsetTop 为元素距离顶部的距离;window.innerHeight 为当前窗口的高度;window.scrollY 为滚动距离;不难知道,当 i.offsetTop <= window.innerHeight + window.scrollY时图片就处于窗口可视区了。
此时效果如下,观察右侧控制台,发现当滚动时图片才加载:
第二种方式其实和第一种差不多,只是计算图片是否在可视区方式不同,重复的部分就省略了,如下:
window.addEventListener("scroll", (e) => { ergodic(); }); function ergodic() { for (let i of images) { //计算方式和第一种方式不同 if (i.getBoundingClientRect().top < window.innerHeight) { let trueSrc = i.getAttribute("data-src"); i.setAttribute("src", trueSrc); } } } ergodic();
其中,getBoundingClientRect().top 为元素相对于窗口的位置;window.innerHeight 为当前窗口的高度;当元素对于窗口的位置小于当前窗口的高度时,那自然处于了窗口可视区了。
效果一样的:
其实上面两种方式已经大致实现懒加载,但是,它们都有一个缺点,就是一当发生滚动事件时,就发生了大量的循环和判断操作判断图片是否可视区里。这自然是不太好的,那是否有解决方法。这里就引入了一个叫 Intersection Observer 观察器接口,它是是浏览器原生提供的构造函数,使用它能省到大量的循环和判断。当然它的兼容可能不太好,看情况使用。
Intersection Observer 是什么呢?这个构造函数的作用是它能够观察可视窗口与目标元素产生的交叉区域。简单来说就是当用它观察我们的图片时,当图片出现或者消失在可视窗口,它都能知道并且会执行一个特殊的回调函数,我们就利用这个回调函数实现我们的操作。概念枯燥难懂,直接看下面例子:
1.既然IntersectionObserver是浏览器原生提供的构造函数,先new一个实例:
const observer = new IntersectionObserver(callback);
其中它会有一个参数callback,参数为一个回调函数,当目标元素能看见会触发一次,目标元素看不见会再触发一次。
2.使用实例通过observer属性可以为每一张图片绑定一个观察器:
for (let i of images) { observer.observe(i); }
3.由上可以知道每张图片能看见会和看不见时都会触发一次callback回调函数,同时callback这个回调函数也有一个参数entries,我们可以运行触发这个回调函数看看它是什么:
function callback(entries) { console.log(entries) }
可以看到每次图片能看见会和看不见时都会触发一次callback回调函数,并输出了参数entries的内容。其实,entries为一个数组,而它的数组元素为当前改变了状态触发了事件的目标元素。其中有一个isIntersecting属性,当目标元素在视口看得见为它true,不在时它为 false 。我们就可以利用这个属性,当它为 true 时设置触发这个事件的图片的src属性值为data-src,开始加载。
function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); } } }
其中 target 事件属性返回触发事件的元素。当前,当回来滚动时,图片会一会可见一会不可见,它都是触发回调函数,所以当某图片已经加载时我们要停掉它的观察器。利用unobserve属性可停掉。
function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); // 结束观察 observer.unobserve(img); } } }
完整代码:
var images = document.getElementsByTagName("img"); function callback(entries) { for (let i of entries) { if (i.isIntersecting) { let img = i.target; let trueSrc = img.getAttribute("data-src"); img.setAttribute("src", trueSrc); observer.unobserve(img); } } } const observer = new IntersectionObserver(callback); for (let i of images) { observer.observe(i); }
效果如下,实现懒加载:
以上就为实现图片懒加载的全部内容啦。✨总的来说就是某些存在大量的img的网页如果页面渲染阶段就把图片全部加载,那会导致页面渲染的堵塞。为了解决引入图片懒加载,用户滚动相应可视区域,若可视区域有图片那么该图片才加载。其中核心就是给图片定义自定义属性data-src存放图片真的访问地址,当图片出现在可视区时才将data-src的值赋值给src属性,此时图片便加载。其中不止不可通过一些常见属性判断图片位置,还引入了IntersectionObserver观察器接口更少消耗的实现懒加载。