懒加载和预加载的目的都是为了提高用户的体验,二者行为是相反的,一个是延迟加载,另一个是提前加载。懒加载对缓解服务器压力有一定作用,预加载则会增长服务器前端压力缓存。
懒加载 lazyload
懒加载:又叫延迟加载、按需加载,当我们打开一个网页,优先展示的首屏图片就先加载,而其他的图片,等到需要去展示的时候再去请求图片资源。
目的:更好的加载页面的首屏内容,对于含有不少图片的比较长的网页来讲,能够加载的更快,避免了首次打开时,一次性加载过多图片资源,是对网页性能的一种优化方式。
原理:浏览器解析到 img 标签的 src 有值,才会去发起请求,那么就可以让图片需要展示时,才对其 src 赋值真正的图片地址。
实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片懒加载</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
margin: 50px;
width: 400px;
height: 200px;
}
</style>
</head>
<body>
<div class="img-container">
<img
src="loading.gif"
alt=""
data-src="1.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="loading.gif"
alt=""
data-src="2.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="loading.gif"
alt=""
data-src="3.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="loading.gif"
alt=""
data-src="4.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="loading.gif"
alt=""
data-src="5.jpg"
class="lazyload"
/>
</div>
<script>
const imgs = [...document.querySelectorAll(".lazyload")]; //img元素转换成数组
lazyload(); //初始调用一次
window.addEventListener("scroll", lazyload, false); //监听滚动时也调用lazyload函数,冒泡阶段
//懒加载
function lazyload() {
for (let i = 0; i < imgs.length; i++) {
const $img = imgs[i]; //每次循环拿到每一个img元素
if (isInVisibleArea($img)) { //判断img元素是否在可视区域内
$img.src = $img.dataset.src; //用data-src替换src属性
imgs.splice(i, 1); //对应该索引的元素删掉
i--;
}
}
}
// 判断DOM元素是否在可视区域内,返回true/false
function isInVisibleArea($el) {
const rect = $el.getBoundingClientRect();
console.log(rect);
return rect.bottom > 0 && rect.top < window.innerHeight && rect.right > 0 && rect.left < window.innerWidth;
}
</script>
</body>
</html>
复制代码
实现细节
1 如何加载图片
用 img 的 data-src 属性存放真正需要显示的图片的路径,当页面滚动直到图片出现在可视区域时,将 data-src 中的图片地址值赋值给 src 属性值。
2 如何判断一个元素出现在可视区域
监听滚动事件,用getBoundingClientRect()
获取 DOM 元素在页面的位置,该函数返回 rect 对象,如下图所示,如果rect.bottom
为负数,rect.top
大于页面高度,或者rect.right
为负数,或者rect.left
大于页面宽度,则认为元素已不在页面的可视区域。
const rect = element.getBoundingClientRect();
复制代码
实现效果
利用开发者工具我们可以看到如下截图:
由于后面三张还没有出现,那么就保持 loading.gif,这样就节省了加载的时间,当我们继续滚动直到出现页面底部,通过开发者工具看到如下的截图:
预加载 preload
预加载:提前加载所需要的图片资源,加载完毕后会缓存到本地,当需要时可以立刻显示出来。
目的:看完一张图片,再看下一张时,会有一段空白的加载时间,如果网络不是很好,加载的时间就会比较久,预加载可以让用户无需等待,获得直接预览的良好体验。
应用场景:看漫画时,如果我们看完 2 了 ,想看 3 ,3 却还没加载完就会大大降低体验感,而如果能在我们预览 2 这段时间里就提前加载好 3 , 等到我们看 3 时就直接里面显示。那么就体验会好很多。参考视频讲解:进入学习
实现方法
1 通过 CSS
步骤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS 预加载</title>
<style>
ul,li{
padding:0;
margin:0;
list-style:none;
}
img {
width: 300px;
}
#preImg1 {
background-image: url('1.jpg');
width: 0;
height: 0; /* 隐藏用来预加载的标签 */
}
#preImg2 {
background-image: url('2.jpg');
width: 0;
height: 0;
}
#preImg3 {
background-image: url('3.jpg');
width: 0;
height: 0;
}
</style>
</head>
<body>
<button onclick="show()">点击显示图片</button>
<ul>
<li id="preImg1"/>
<li id="preImg2"/>
<li id="preImg3"/>
</ul>
</body>
<script>
const show = function () {
document.querySelector("ul").innerHTML = `
<li><img src="1.jpg"></li>
<li><img src="2.jpg"></li>
<li><img src="3.jpg"></li>`
}
</script>
</html>
复制代码
2 通过 JavaScript
步骤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片预加载</title>
<style>
.img-container {
display: flex;
align-items: center;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
}
img {
width: 100%;
}
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div class="img-container">
<img src="1.jpg" alt="" id="img" />
</div>
<script>
const imgs = [
"2.jpg",
"3.jpg",
"4.jpg",
"5.jpg",
];
let i = 0;
const $img = document.getElementById("img");
// 页面一开始调用preload,加载数组的第一个元素
preload(imgs[i])
.then((data) => {})
.catch(() => {});
// 点击切换
$img.addEventListener(
"click",
() => {
if (i < imgs.length) {
$img.src = imgs[i]; // 将数组元素的src赋值给页面元素
i++;
if (i < imgs.length) {
preload(imgs[i]); // 当索引小于数组length,预加载下一个图片
}
} else {
console.log("已经是最后一张了!"); // 当 索引和 数组length相同 则数组内没元素了
}
},
false
);
// 预加载
function preload(src) {
return new Promise((resolve, reject) => {
const image = new Image(); // 创建一个新的图片标签
image.addEventListener("load", () => resolve(image), false); // 图片加载完成调用成功状态
image.addEventListener("error", () => reject(), false); // 图片加载失败调用失败状态
image.src = src; // 将传进来的src赋值给新的图片
});
}
</script>
</body>
</html>
复制代码
实现效果
总结对比
评论