"你们的网站加载速度太慢了,而且 SEO 效果很差。"上个月,我们接到了一个来自海外客户的紧急需求。他们的电商网站是用传统的 React SPA 构建的,在性能和搜索引擎优化方面都遇到了瓶颈。作为技术负责人,我立刻想到了 Next.js 14 的 App Router。????
今天,我想和大家分享这个项目的重构经历。从技术选型到实际落地,我们是如何一步步优化网站性能的。希望能给同样面临类似挑战的朋友一些启发!
为什么选择 Next.js 14 的 App Router?
说实话,最开始团队里对于是否要用 App Router 是有分歧的。有同事担心它太新,文档不够完善。但经过深入研究,我们发现 App Router 完美解决了我们的痛点:
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<Header /> {/* 这是一个服务器组件 */}
{children}
<Footer /> {/* 这也是一个服务器组件 */}
</body>
</html>
)
}
这种基于文件系统的路由方式不仅直观,更重要的是它默认使用服务器组件(Server Components)。这意味着大部分组件都在服务器端渲染,显著减少了发送到浏览器的 JavaScript 代码量。
性能优化实践
1. 合理使用服务器组件和客户端组件
在重构过程中,我们发现一个关键点:不是所有组件都适合作为服务器组件。这里分享一个我们总结的判断标准:
// app/products/[id]/page.tsx
import { ProductDetails } from './ProductDetails' // 服务器组件
import { AddToCartButton } from './AddToCartButton' // 客户端组件
export default async function ProductPage({ params }: { params: { id: string } }) {
// 直接在服务器端获取数据
const product = await getProduct(params.id)
return (
<div className="product-page">
{/* 静态内容使用服务器组件 */}
<ProductDetails product={product} />
{/* 交互部分使用客户端组件 */}
<AddToCartButton productId={product.id} />
</div>
)
}
2. 数据获取策略优化
Next.js 14 提供了强大的数据获取功能。我们充分利用了这一点:
// utils/products.ts
export async function getProducts() {
// ???? 使用 Next.js 的缓存机制
const res = await fetch('https://api.mystore.com/products', {
next: {
revalidate: 3600 // 每小时重新验证一次
}
})
if (!res.ok) {
// 错误处理很重要!
throw new Error('Failed to fetch products')
}
return res.json()
}
3. 图片优化和布局转移
Next.js 的 Image 组件是一个宝藏,它帮我们解决了很多性能问题:
// components/ProductImage.tsx
import Image from 'next/image'
export function ProductImage({ src, alt }: { src: string; alt: string }) {
return (
<div className="relative w-full aspect-square">
<Image
src={src}
alt={alt}
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
priority={false}
className="object-cover"
/>
</div>
)
}
优化后的效果立竿见影:
- 首次内容渲染(FCP)从 2.8s 降到了 0.8s
- 最大内容绘制(LCP)从 4.2s 降到了 1.5s
- Core Web Vitals 全部达到了绿色标准
踩坑经历和解决方案
开发过程中确实遇到了一些挑战,分享几个印象深刻的:
1. 状态管理的取舍
最开始我们想当然地把所有状态都放在了客户端。后来发现这样做反而影响了性能,于是我们采用了一个混合方案:
// app/products/layout.tsx
export default function ProductsLayout({
children,
modal
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<>
{/* 主内容区使用服务器组件 */}
{children}
{/* 模态框使用客户端组件 */}
{modal}
</>
)
}
2. 路由预加载策略
Next.js 的预加载功能很强大,但需要合理使用:
// components/ProductLink.tsx
'use client'
import { useCallback } from 'react'
import { useRouter } from 'next/navigation'
export function ProductLink({ id }: { id: string }) {
const router = useRouter()
// 使用 useCallback 优化性能
const handleClick = useCallback(() => {
// 预加载下一页数据
router.prefetch(`/products/${id}`)
}, [id, router])
return (
<button
onClick={handleClick}
onMouseEnter={handleClick} // 鼠标悬停时预加载
>
查看详情
</button>
)
}
3. 缓存策略的优化
缓存是一个容易被忽视的优化点。我们通过实践总结出了一套规则:
// app/api/products/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
try {
const products = await getProducts()
// 设置适当的缓存头
return NextResponse.json(products, {
headers: {
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400'
}
})
} catch (error) {
console.error('Failed to fetch products:', error)
return NextResponse.error()
}
}
项目成果
经过一个月的重构,我们的网站性能得到了显著提升:
- Google PageSpeed Insights 分数从 65 提升到 95
- 用户平均访问时长增加了 40%
- 跳出率下降了 25%
- 转化率提升了 15%
最让我欣慰的是收到客户的反馈:"网站速度快得惊人,感觉像是本地应用一样流畅!"这让所有的努力都值得了。????
写在最后
Next.js 14 的 App Router 确实是一个革命性的更新,它让我们能够用更现代的方式构建 React 应用。如果你也在考虑是否要升级到 App Router,我的建议是:先从小功能开始尝试,逐步积累经验,最后再考虑大规模重构。
有什么问题欢迎在评论区讨论,我们一起学习进步!
如果觉得有帮助,别忘了点赞关注,我会继续分享更多 Next.js 和 React 的实战经验~