对于页面中大量或高质量的图片,如何高效加载?两种常见的优化图片加载性能的策略便是懒加载(Lazy Load)和预加载(Preload)。
一、懒加载(Lazy Load)
1. 懒加载的原理
懒加载是一种延迟加载技术,意味着只有当图片进入可视区域时,才会发起网络请求去加载该图片资源。它的基本思路是:在页面加载时,只加载可视区域内的图片,其他部分的图片则等到用户滚动页面时再加载。这样可以减少初始页面加载的图片数量,从而提升页面加载速度和减少服务器压力。
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 {
height: 300px;
display: block;
}
</style>
</head>
<body>
<img src="" data-src="https://example.com/image1.gif" alt="">
<img src="" data-src="https://example.com/image2.gif" alt="">
<!-- 更多图片 -->
<script>
let height = window.innerHeight
// 判断默认情况下应该展示那些图片
function lazyLoad() {
let imgs = document.querySelectorAll('img[data-src]')
for (let i = 0; i < imgs.length; i++) {
let rect = imgs[i].getBoundingClientRect() // 获取元素的集合属性
if (rect.bottom > 0 && rect.top < height) { // 判断元素底部和顶部距离视窗顶部的距离
// imgs[i].src = imgs[i].getAttribute('data-src')
let newImg = new Image()
newImg.src = imgs[i].getAttribute('data-src')
newImg.onload = function () { // 图片被浏览器加载完毕
imgs[i].src = newImg.getAttribute('src') //浏览器的img不出现在body里面,只要src有值,也会去发送请求。其他的,比如script,src有值,只要没有出现在body里面,就不会去请求加载
}
imgs[i].removeAttribute('data-src')
}
}
}
lazyLoad()
window.addEventListener('scroll', lazyLoad)
</script>
</body>
</html>
这里也可以使用IntersectionObserver API来监听元素在视口中的可见度变化从而判断图片是否进入可视区域。
3. 懒加载的优缺点
优点:
- 提高页面加载速度: 只有可视区域内的图片才会加载,其他图片延迟加载,减少了初始加载的资源消耗。
- 节省带宽: 用户如果没有滚动到某些图片所在位置,这些图片不会被加载,从而节省带宽和流量。
缺点:
- 短暂的白屏: 当用户滚动到图片时,可能会有短暂的空白,直到图片加载完成。
- 影响SEO: 如果搜索引擎爬虫不支持懒加载,可能会影响页面的索引,导致图片资源无法被抓取。
4. 适用场景
- 图片较多的页面: 适合那些包含大量图片或大尺寸图片的页面,比如商品展示页、图片库、社交媒体动态等。
- 优化移动端: 在移动端,用户通常网络速度较慢,懒加载能显著提升体验。
二、预加载(Preload)
1. 预加载的原理
预加载是一种提前加载图片资源的策略,它会在页面加载时,利用额外的线程去加载一些用户可能会查看的图片。当用户滚动到这些图片时,它们已经在浏览器缓存中,从而快速展示。预加载可以通过JavaScript的Worker线程和XMLHttpRequest来实现,减少主线程的阻塞。
2. 预加载的实现
预加载的核心思想是创建一个新的线程,利用这个线程提前请求图片资源。在图片加载完毕后,将图片资源交给主线程来显示。以下是一个预加载的实现示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>预加载示例</title>
</head>
<body>
<div id="pic"></div>
<script>
let pic = document.getElementById('pic');
let arr = [
"https://example.com/image1.gif",
"https://example.com/image2.gif",
"https://example.com/image3.gif",
// 更多在线图片链接
];
// 创建一个新的线程
const worker = new Worker('worker.js')
// 将数据发送给子线程
worker.postMessage(arr)
// 接收子线程发送的数据
worker.onmessage = function (e) {
console.log(e.data);
const img = new Image()
// console.log(window.URL.createObjectURL(e.data));
img.src = window.URL.createObjectURL(e.data)
pic.appendChild(img) //预加载图片加载不是按顺序,先请求到先展示(可优化)
}
</script>
</body>
</html>
在分线程worker.js中,代码会如下所示:
self.onmessage = function(e) {
// console.log(e.data);
// 将数组中的地址资源加载出来
let arr = e.data;
for (let i = 0; i < arr.length; i++) {
let xhr = new XMLHttpRequest();
xhr.open("get", arr[i], true);
xhr.responseType = 'blob' // 返回blob文件类型
xhr.send();
xhr.onload = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
// console.log(xhr.response);
self.postMessage(xhr.response); // 返回blob文件类型
}
}
}
}
// 非主线程不能用 new Image()
此时预加载完成,前端会向本地请求图片资源。
3. 预加载的优缺点
优点:
- 避免滚动时的白屏: 由于图片资源在后台加载完成,用户滚动到相应位置时不会看到加载中的空白。
缺点:
- 增加服务器压力: 在页面加载时就请求了所有图片,可能导致短时间内对服务器的请求量激增,尤其是当图片资源非常多时。
- 浪费带宽: 预加载的图片可能是用户最终不会查看的,浪费带宽和流量。
4. 适用场景
- 图片数量有限且较小的页面: 适合那些图片数量不多且大小较小的页面,预加载可以提高体验而不会给服务器带来过大压力。
三、懒加载与预加载的比较
| 特性 | 懒加载 | 预加载 |
|---|---|---|
| 加载时机 | 图片进入可视区域时加载 | 页面加载时即开始加载所有图片 |
| 性能影响 | 减少初始加载时间,节省带宽 | 可能增加页面加载时间和服务器压力 |
| 用户体验 | 图片过大时,滚动时可能有短暂的白屏 | 用户滚动时图片立即显示,无延迟或空白区域 ,但首屏加载可能出现稍长时间白屏 |