By AriesZhou · · 3 min read
Lazy Loading Guide: Essential Techniques for Page Performance
Performance Optimization
Open an image-heavy page and wait several seconds before anything appears? First-screen load time too long, hurting user experience and SEO? These are exactly the problems lazy loading solves. Lazy loading is a core front-end performance optimization technique. By deferring non-first-screen resources, it significantly improves initial load speed and user experience.
1. How lazy loading works
graph LR
A[首屏渲染] --> B[只加载可见内容]
B --> C[用户滚动]
C --> D[触发加载]
D --> E[加载视口内资源]
E --> C
Core idea: Load on demand, only when needed.
2. Image lazy loading
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 Image lazy loading in 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 component lazy loading
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 Route lazy loading
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 dynamic imports
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 code splitting configuration
// 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. Best practices
5.1 Placeholders
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 Reserved dimensions
/* 为图片预留空间,避免布局偏移 */
.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 Progressive loading
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. Summary
Lazy loading is a key technique for improving performance:
- Image lazy loading: using the Intersection Observer API
- Component lazy loading: React.lazy + Suspense
- Route lazy loading: Load route modules on demand
- Code splitting: Webpack dynamic imports
Using lazy loading wisely can significantly improve first-screen load speed and user experience.