· 3 min read
懒加载完全指南:提升页面性能的必备技术
性能优化
打开一个图片丰富的页面,结果等待好几秒才能看到内容?首屏加载时间太长,影响用户体验和 SEO?这些都是懒加载可以解决的问题。懒加载(Lazy Loading)是前端性能优化的核心技术。通过延迟加载非首屏资源,显著提升首屏加载速度和用户体验。
1. 懒加载原理
graph LR
A[首屏渲染] --> B[只加载可见内容]
B --> C[用户滚动]
C --> D[触发加载]
D --> E[加载视口内资源]
E --> C
核心思想:按需加载,只在需要时加载。
2. 图片懒加载
2.1 Intersection Observer API
const observer = new IntersectionObserver((entries, observer) => {
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);
});
2.2 React 中的图片懒加载
import { useEffect, useState, useRef } from 'react';
function LazyImage({ src, alt, placeholder }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isInView, setIsInView] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setIsInView(true);
observer.disconnect();
}
});
});
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef} style={{ minHeight: '200px', background: '#f0f0f0' }}>
{isInView && (
<img
src={src}
alt={alt}
style={{ opacity: isLoaded ? 1 : 0, transition: 'opacity 0.3s' }}
onLoad={() => setIsLoaded(true)}
/>
)}
</div>
);
}
3. React 组件懒加载
3.1 React.lazy + Suspense
import { Suspense, lazy } from 'react';
// 懒加载组件
const HeavyChart = lazy(() => import('./HeavyChart'));
const Modal = lazy(() => import('./Modal'));
const Editor = lazy(() => import('./Editor'));
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<h1>我的应用</h1>
<Suspense fallback={<LoadingSpinner />}>
<HeavyChart data={data} />
</Suspense>
<button onClick={() => setShowModal(true)}>打开弹窗</button>
{showModal && (
<Suspense fallback={<ModalLoading />}>
<Modal onClose={() => setShowModal(false)} />
</Suspense>
)}
</div>
);
}
3.2 路由懒加载
import { Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<PageLoading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
4. JavaScript 动态导入
4.1 ES6 import()
// 点击按钮时才加载模块
button.addEventListener('click', async () => {
const { doSomething } = await import('./utils.js');
doSomething();
});
// 条件加载
if (condition) {
const { heavyModule } = await import('./heavy.js');
}
4.2 Webpack 代码分割配置
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
minChunks: 2,
priority: -10,
reuseExistingChunk: true,
},
},
},
},
};
5. 最佳实践
5.1 占位符
function ImageWithPlaceholder({ src, alt }) {
return (
<div style={{ position: 'relative', minHeight: '300px' }}>
{/* 加载前显示占位 */}
<div
style={{
position: 'absolute',
inset: 0,
background: 'linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)',
backgroundSize: '200% 100%',
animation: 'shimmer 1.5s infinite'
}}
/>
<img src={src} alt={alt} loading="lazy" />
</div>
);
}
5.2 预留尺寸
/* 为图片预留空间,避免布局偏移 */
.lazy-image-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 比例 */
}
.lazy-image-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
5.3 渐进式加载
function ProgressiveImage({ lowResSrc, highResSrc, alt }) {
const [loaded, setLoaded] = useState(false);
return (
<div>
<img
src={lowResSrc}
alt={alt}
style={{ filter: loaded ? 'none' : 'blur(10px)', transition: 'filter 0.3s' }}
/>
<img
src={highResSrc}
alt={alt}
style={{ opacity: loaded ? 1 : 0, transition: 'opacity 0.3s' }}
onLoad={() => setLoaded(true)}
/>
</div>
);
}
6. 总结
懒加载是提升性能的关键技术:
- 图片懒加载: 使用 Intersection Observer API
- 组件懒加载: React.lazy + Suspense
- 路由懒加载: 按需加载路由模块
- 代码分割: Webpack 动态导入
合理使用懒加载,能显著提升首屏加载速度和用户体验。