渲染大量图片时,首屏加载性能至关重要。以下是全面的优化方案:
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Fallback">
picture>
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="...">
<script>
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
script>
// 使用react-window或vue-virtual-scroller等库
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style}>
<img src={items[index].image} alt={`Item ${index}`} />
</div>
);
<List height={600} itemCount={1000} itemSize={150} width={300}>
{Row}
</List>
低质量图片占位(LQIP):
<img
src="image-lqip.jpg"
data-src="image-hd.jpg"
class="lazyload blur-up"
alt="..."
>
<style>
.blur-up {
filter: blur(5px);
transition: filter 0.3s;
}
.blur-up.lazyloaded {
filter: blur(0);
}
style>
纯色/渐变占位:
<div
style="background: linear-gradient(to right, #f6f7f8, #e9e9e9)"
data-src="real-image.jpg"
class="lazyload-placeholder"
>div>
<img
srcset="small.jpg 480w, medium.jpg 768w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 1200px) 768px, 1200px"
src="fallback.jpg"
alt="Responsive image"
>
https://cdn.example.com/image.jpg?width=800&format=webp&quality=80
Cache-Control: public, max-age=31536000, immutable
Link: ; rel=preload; as=image
// 使用Progressive Image库
import ProgressiveImage from 'react-progressive-image';
<ProgressiveImage src="large.jpg" placeholder="tiny.jpg">
{(src, loading) => (
<img
style={{ opacity: loading ? 0.5 : 1 }}
src={src}
alt="渐进加载"
/>
)}
</ProgressiveImage>
// 在Worker中预加载图片
const worker = new Worker('image-loader.js');
worker.postMessage({ images: imageUrls });
DOCTYPE html>
<html>
<head>
<title>图片加载优化title>
<style>
.image-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
.image-wrapper {
aspect-ratio: 16/9;
background: #f0f0f0;
position: relative;
overflow: hidden;
}
.lazy-image {
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0;
transition: opacity 0.3s;
}
.lazy-image.loaded {
opacity: 1;
}
.spinner {
/* 加载动画样式 */
}
style>
head>
<body>
<div class="image-container" id="gallery">div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const gallery = document.getElementById('gallery');
const imageUrls = [...]; // 你的图片URL数组
// 初始加载首屏图片
loadInitialImages();
// 滚动加载剩余图片
window.addEventListener('scroll', throttle(loadMoreImages, 200));
function loadInitialImages() {
const viewportHeight = window.innerHeight;
const initialLoadCount = Math.ceil(viewportHeight / 250) * 4;
imageUrls.slice(0, initialLoadCount).forEach(url => {
createImageElement(url);
});
}
function loadMoreImages() {
const scrollPosition = window.scrollY + window.innerHeight;
const galleryBottom = gallery.offsetTop + gallery.offsetHeight;
if (scrollPosition > galleryBottom - 500) {
const loadedCount = document.querySelectorAll('.lazy-image').length;
const nextBatch = imageUrls.slice(loadedCount, loadedCount + 10);
nextBatch.forEach(url => {
createImageElement(url);
});
}
}
function createImageElement(url) {
const wrapper = document.createElement('div');
wrapper.className = 'image-wrapper';
const img = document.createElement('img');
img.className = 'lazy-image';
img.dataset.src = url;
img.alt = 'Gallery image';
const spinner = document.createElement('div');
spinner.className = 'spinner';
wrapper.appendChild(spinner);
wrapper.appendChild(img);
gallery.appendChild(wrapper);
// 使用Intersection Observer懒加载
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.onload = () => {
lazyImage.classList.add('loaded');
spinner.remove();
};
observer.unobserve(lazyImage);
}
});
});
observer.observe(img);
}
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
});
script>
body>
html>
性能指标监控
A/B测试不同方案
用户网络自适应
// 根据网络状况调整图片质量
if (navigator.connection) {
const effectiveType = navigator.connection.effectiveType;
const imgQuality = effectiveType === '4g' ? 'high' : 'low';
// 加载对应质量的图片
}
通过组合这些策略,可以显著提升大量图片场景下的首屏加载性能,提供更好的用户体验。