name: 前端卓越 description: 用于React Server Components、性能优化和Core Web Vitals的现代前端模式
前端卓越
React Server Components
服务器组件在服务器上运行,并将渲染的HTML发送到客户端。它们可以直接访问数据库、文件系统和内部API,而无需将其暴露给浏览器。
// app/products/page.tsx (默认是服务器组件)
async function ProductsPage() {
const products = await db.query("SELECT * FROM products WHERE active = true");
return (
<main>
<h1>Products</h1>
<ProductList products={products} />
<AddToCartButton /> {/* 客户端组件 */}
</main>
);
}
规则:
- 服务器组件不能使用
useState、useEffect或浏览器API - 在文件顶部用
'use client'标记交互组件 - 从服务器组件传递可序列化的props到客户端组件(不能传递函数或类)
- 尽量将
'use client'边界保持在树中尽可能深的位置
Streaming SSR
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<Header /> {/* 立即渲染 */}
<Suspense fallback={<ChartSkeleton />}>
<AnalyticsChart /> {/* 准备好时流式传输 */}
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders /> {/* 独立流式传输 */}
</Suspense>
</div>
);
}
每个 Suspense 边界独立流式传输。将边界放在数据获取组件周围,以避免阻塞整个页面。
代码分割
import dynamic from 'next/dynamic';
const HeavyEditor = dynamic(() => import('@/components/Editor'), {
loading: () => <EditorSkeleton />,
ssr: false,
});
const AdminPanel = dynamic(() => import('@/components/AdminPanel'));
分割点:
- 路由边界(Next.js App Router 中自动)
- 条件渲染的组件(模态框、抽屉、管理面板)
- 重型库(图表库、富文本编辑器、地图)
- 首屏下方内容
捆绑优化
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react', 'lodash-es'],
},
};
检查清单:
- 运行
npx next build并查看每个路由的输出大小 - 使用
@next/bundle-analyzer识别大型依赖 - 用
date-fns或dayjs替换moment(节省约200KB) - 导入特定函数:
import { debounce } from 'lodash-es/debounce' - 优先使用CSS而非JS进行动画(无运行时成本)
- 树摇图标库:
import { Search } from 'lucide-react'
Core Web Vitals 目标
| 指标 | 良好 | 需要改进 | 差 |
|---|---|---|---|
| LCP (最大内容绘制) | <2.5秒 | 2.5-4.0秒 | >4.0秒 |
| INP (交互到下一次绘制) | <200毫秒 | 200-500毫秒 | >500毫秒 |
| CLS (累计布局偏移) | <0.1 | 0.1-0.25 | >0.25 |
LCP 优化
- 预加载英雄图片:
<link rel="preload" as="image" href="..." /> - 在首屏
<Image>组件上使用priority属性 - 内联关键CSS,延迟非关键样式表
- 为首屏内容避免客户端渲染
- 在图片上设置显式的
width/height以防止布局偏移
图像优化
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="描述性替代文本"
width={1200}
height={630}
priority // 为LCP图片预加载
sizes="(max-width: 768px) 100vw, 50vw"
placeholder="blur"
blurDataURL={base64} // 内联微小占位符
/>
- 使用
next/image或等效库(自动WebP/AVIF,响应式srcset) - 设置
sizes属性以避免下载过大的图片 - 使用
placeholder="blur"与base64数据URL以提升感知性能 - 延迟加载首屏下方图片(默认行为)
字体加载策略
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // 立即显示后备字体
preload: true,
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html className={inter.variable}>
<body>{children}</body>
</html>
);
}
- 使用
next/font进行零CLS字体加载,带自动子集 - 设置
display: 'swap'以避免加载期间文本不可见 - 自托管字体而非从Google CDN加载(节省DNS查找)
- 最多限制为2个字体族
CLS 预防
- 始终在图片和视频上设置
width和height - 使用
aspect-ratioCSS 用于响应式媒体容器 - 为动态内容(广告、嵌入)预留空间,使用
min-height - 避免在加载后向现有内容上方插入内容
- 使用CSS
contain: layout用于改变大小的组件
性能监控
import { onCLS, onINP, onLCP } from 'web-vitals';
onCLS(console.log);
onINP(console.log);
onLCP(console.log);
测量真实用户指标(RUM),而不仅仅是实验室分数。Vercel Analytics 和 Google Search Console 提供现场数据。