心遠愈知 發表於 2024-12-12 10:17:00

Next.js 14 实战:使用 App Router 构建高性能 React 应用

<p>"你们的网站加载速度太慢了,而且 SEO 效果很差。"上个月,我们接到了一个来自海外客户的紧急需求。他们的电商网站是用传统的 React SPA 构建的,在性能和搜索引擎优化方面都遇到了瓶颈。作为技术负责人,我立刻想到了 Next.js 14 的 App Router。😊</p>
<p>今天,我想和大家分享这个项目的重构经历。从技术选型到实际落地,我们是如何一步步优化网站性能的。希望能给同样面临类似挑战的朋友一些启发!</p>
<h2 id="为什么选择-nextjs-14-的-app-router">为什么选择 Next.js 14 的 App Router?</h2>
<p>说实话,最开始团队里对于是否要用 App Router 是有分歧的。有同事担心它太新,文档不够完善。但经过深入研究,我们发现 App Router 完美解决了我们的痛点:</p>
<pre><code class="language-typescript">// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
    &lt;html lang="en"&gt;
      &lt;body&gt;
      &lt;Header /&gt; {/* 这是一个服务器组件 */}
      {children}
      &lt;Footer /&gt; {/* 这也是一个服务器组件 */}
      &lt;/body&gt;
    &lt;/html&gt;
)
}
</code></pre>
<p>这种基于文件系统的路由方式不仅直观,更重要的是它默认使用服务器组件(Server Components)。这意味着大部分组件都在服务器端渲染,显著减少了发送到浏览器的 JavaScript 代码量。</p>
<h2 id="性能优化实践">性能优化实践</h2>
<h3 id="1-合理使用服务器组件和客户端组件">1. 合理使用服务器组件和客户端组件</h3>
<p>在重构过程中,我们发现一个关键点:不是所有组件都适合作为服务器组件。这里分享一个我们总结的判断标准:</p>
<pre><code class="language-typescript">// app/products//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 (
    &lt;div className="product-page"&gt;
      {/* 静态内容使用服务器组件 */}
      &lt;ProductDetails product={product} /&gt;
      
      {/* 交互部分使用客户端组件 */}
      &lt;AddToCartButton productId={product.id} /&gt;
    &lt;/div&gt;
)
}
</code></pre>
<h3 id="2-数据获取策略优化">2. 数据获取策略优化</h3>
<p>Next.js 14 提供了强大的数据获取功能。我们充分利用了这一点:</p>
<pre><code class="language-typescript">// 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()
}
</code></pre>
<h3 id="3-图片优化和布局转移">3. 图片优化和布局转移</h3>
<p>Next.js 的 Image 组件是一个宝藏,它帮我们解决了很多性能问题:</p>
<pre><code class="language-typescript">// components/ProductImage.tsx
import Image from 'next/image'

export function ProductImage({ src, alt }: { src: string; alt: string }) {
return (
    &lt;div className="relative w-full aspect-square"&gt;
      &lt;Image
      src={src}
      alt={alt}
      fill
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      priority={false}
      className="object-cover"
      /&gt;
    &lt;/div&gt;
)
}
</code></pre>
<p>优化后的效果立竿见影:</p>
<ul>
<li>首次内容渲染(FCP)从 2.8s 降到了 0.8s</li>
<li>最大内容绘制(LCP)从 4.2s 降到了 1.5s</li>
<li>Core Web Vitals 全部达到了绿色标准</li>
</ul>
<h2 id="踩坑经历和解决方案">踩坑经历和解决方案</h2>
<p>开发过程中确实遇到了一些挑战,分享几个印象深刻的:</p>
<h3 id="1-状态管理的取舍">1. 状态管理的取舍</h3>
<p>最开始我们想当然地把所有状态都放在了客户端。后来发现这样做反而影响了性能,于是我们采用了一个混合方案:</p>
<pre><code class="language-typescript">// app/products/layout.tsx
export default function ProductsLayout({
children,
modal
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
    &lt;&gt;
      {/* 主内容区使用服务器组件 */}
      {children}
      
      {/* 模态框使用客户端组件 */}
      {modal}
    &lt;/&gt;
)
}
</code></pre>
<h3 id="2-路由预加载策略">2. 路由预加载策略</h3>
<p>Next.js 的预加载功能很强大,但需要合理使用:</p>
<pre><code class="language-typescript">// 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(() =&gt; {
    // 预加载下一页数据
    router.prefetch(`/products/${id}`)
}, )

return (
    &lt;button
      onClick={handleClick}
      onMouseEnter={handleClick} // 鼠标悬停时预加载
    &gt;
      查看详情
    &lt;/button&gt;
)
}
</code></pre>
<h3 id="3-缓存策略的优化">3. 缓存策略的优化</h3>
<p>缓存是一个容易被忽视的优化点。我们通过实践总结出了一套规则:</p>
<pre><code class="language-typescript">// 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()
}
}
</code></pre>
<h2 id="项目成果">项目成果</h2>
<p>经过一个月的重构,我们的网站性能得到了显著提升:</p>
<ul>
<li>Google PageSpeed Insights 分数从 65 提升到 95</li>
<li>用户平均访问时长增加了 40%</li>
<li>跳出率下降了 25%</li>
<li>转化率提升了 15%</li>
</ul>
<p>最让我欣慰的是收到客户的反馈:"网站速度快得惊人,感觉像是本地应用一样流畅!"这让所有的努力都值得了。😊</p>
<h2 id="写在最后">写在最后</h2>
<p>Next.js 14 的 App Router 确实是一个革命性的更新,它让我们能够用更现代的方式构建 React 应用。如果你也在考虑是否要升级到 App Router,我的建议是:先从小功能开始尝试,逐步积累经验,最后再考虑大规模重构。</p>
<p>有什么问题欢迎在评论区讨论,我们一起学习进步!</p>
<blockquote>
<p>如果觉得有帮助,别忘了点赞关注,我会继续分享更多 Next.js 和 React 的实战经验~</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/yuanyanglu/p/18601745
頁: [1]
查看完整版本: Next.js 14 实战:使用 App Router 构建高性能 React 应用