在现代 Web 应用中,页面首次加载性能(FP、FCP、LCP)对用户体验和转化率影响巨大。假设我们起初页面加载时间长达 8 秒,跳出率高、用户流失严重。通过本文所述 12 个关键操作,实际将页面优化至约 1 秒的加载时长。
性能指标 | 说明 | 建议值 |
|---|---|---|
FCP(首次内容绘制) | 首次绘制任何内容的时间 | < 1s |
LCP(最大内容绘制) | 主内容加载完成时间 | < 2.5s |
TTI(可交互时间) | 页面可以响应用户输入的时间点 | < 3s |
TBT(总阻塞时间) | 脚本执行阻塞主线程的时间 | < 300ms |
CLS(累积布局偏移) | 页面视觉稳定性 | < 0.1 |
懒加载(Lazy Load)让资源仅在需要时加载,避免首屏资源过大。
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="..." />或使用 JavaScript:
document.querySelectorAll('img[data-src]').forEach(img => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
img.src = img.dataset.src;
observer.unobserve(img);
}
});
observer.observe(img);
});optimization: {
usedExports: true, // 启用 tree shaking
concatenateModules: true, // 开启 scope hoisting
}HTTP/2 支持一个连接并发多个请求,减少连接开销,提高资源加载并行度。
listen 443 ssl http2;现代图像格式(WebP、AVIF)在质量相当下体积显著减小
<picture>
<source srcset="banner.avif" type="image/avif">
<source srcset="banner.webp" type="image/webp">
<img src="banner.jpg" alt="banner" />
</picture>按需加载功能模块,避免主包体积臃肿。
const Dashboard = React.lazy(() => import('./Dashboard'));
<Route path="/dashboard" element={
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
} />// pages/index.js
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data } };
}或静态生成:
export async function getStaticProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data }, revalidate: 60 }; // ISR
}渲染方式 | FCP | LCP | SEO 友好性 |
|---|---|---|---|
CSR(客户端渲染) | 慢 | 慢 | 差 |
SSR | 快 | 中 | 良好 |
SSG | 极快 | 极快 | 优秀 |
Cache-Control、ETag 实现浏览器与 CDN 缓存gzip on;
gzip_types text/plain text/css application/json application/javascript;
location ~* \.(js|css|png|jpg|webp|avif)$ {
expires 1y;
cache-control: public;
}仅将首屏需要的关键 CSS 内联到 HTML,其余异步加载,提升渲染速度。
critters 插件)npm install critters-webpack-pluginconst Critters = require('critters-webpack-plugin');
plugins: [new Critters({ preload: 'swap' })]或手动内联:
<style>
/* critical css */
body { margin: 0; font-family: sans-serif; }
</style>
<link rel="stylesheet" href="main.css" media="print" onload="this.media='all'">字体文件往往体积大,延迟渲染。优化手段包括:
font-display: swap@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
}pyftsubset:字体子集提取preconnect 与 dns-prefetch提前建立 DNS 和 TCP/TLS 连接,加速第三方资源加载。
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>大任务阻塞主线程导致卡顿,通过任务切片与多线程解耦计算逻辑。
requestIdleCallback 切片执行任务function chunkTask(arr, callback, chunkSize = 100) {
let i = 0;
function nextChunk(deadline) {
while (i < arr.length && deadline.timeRemaining() > 0) {
callback(arr[i++]);
}
if (i < arr.length) requestIdleCallback(nextChunk);
}
requestIdleCallback(nextChunk);
}const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => {
console.log('Result:', e.data);
};"ci": {
"collect": {
"url": ["https://example.com"],
"numberOfRuns": 3
},
"assert": {
"assertions": {
"first-contentful-paint": ["error", { "maxNumericValue": 1000 }]
}
}
}const observer = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
console.log(entry.name, entry.startTime);
});
});
observer.observe({ entryTypes: ['paint', 'longtask'] });优化策略 | 关键指标改善 | 典型收益 |
|---|---|---|
懒加载 & 延迟加载 | FCP, LCP | 首屏加载快 30% |
Tree Shaking | TTI, 包体积 | 减少冗余代码 |
HTTP/2 | TTFB, 并发加载 | 资源加载加速 |
WebP / AVIF | LCP, 网络流量 | 资源减半 |
Code Splitting | TTI | 首包减小 |
SSR / SSG | FCP, SEO | 首屏更快,SEO 提升 |
Gzip / Brotli | 全面 | 减小传输体积 |
Critical CSS | FCP | 样式提前渲染 |
Font 优化 | CLS, FCP | 避免字体闪烁 |
预连接优化 | TTFB | 延迟下降 |
Worker 优化 | TBT | 执行不卡顿 |
性能预算 | 全面 | 自动守门人机制 |
工具 | 功能 | 特点 |
|---|---|---|
Lighthouse | 性能审计 | 可集成到 CI/CD |
WebPageTest | 加载链路详细分析 | 可视化强 |
Chrome DevTools | 运行时性能分析 | 实时调试 |
Sentry + WebVitals | 实时用户监控 | 可视化仪表盘 |
SpeedCurve | 性能趋势监控 | 商业服务 |