Next.js 16 App Router 最佳实践:10 条核心规则
Next.js 16 + App Router 的最佳实践清单。本文提炼 10 条核心规则,帮你避免常见坑,让项目结构更优雅。
今日技术简讯
📰 技术简讯 · 2026-06-13
今日聚合 7 条热门技术内容。
🤖 AI / LLM
1. Anthropic 发布 Prompt Caching
- 链接:https://www.anthropic.com/news/prompt-caching
- 来源:Anthropic 官方
- 摘要:Prompt Caching 让重复使用的大 prompt 成本降低 90%、延迟降低 80%。
2. Groq 推出 Llama 4 推理服务
- 链接:https://groq.com/llama-4
- 来源:Groq
- 摘要:Llama 4 70B 推理速度 1200 tokens/秒,延迟极低。
🎨 前端 / Web
3. Next.js 16 App Router 最佳实践
- 链接:https://nextjs.org/docs/app/best-practices
- 来源:Vercel
- 摘要:Next.js 16 官方整理的 App Router 最佳实践清单,10 条核心规则。
4. React Router 7 正式发布
- 链接:https://reactrouter.com
- 来源:Remix 团队
- 摘要:React Router 7 整合 Remix 特性,单页应用 + SSR 双模式。
⚙️ 后端 / 架构
5. Apache Kafka 4.0 发布
- 链接:https://kafka.apache.org/40
- 来源:Apache Kafka
- 摘要:Kafka 4.0 引入 KRaft 默认模式,不再依赖 ZooKeeper。
🚀 独立开发 / OPC
6. Framer Motion 改名 Motion
- 链接:https://motion.dev
- 来源:Motion 团队
- 摘要:Framer Motion 改名为 Motion,独立产品定位更清晰。
7. Paddle 推出 Billing 2.0
- 链接:https://www.paddle.com/billing-2
- 来源:Paddle
- 摘要:Merchant of Record 模式继续领跑,开发者专注业务,回避税务。
数据来源:HN / Reddit / 各厂博客 采集时间:2026-06-13 09:00 (UTC+8)
今日深度文
Next.js 16 App Router 最佳实践:10 条核心规则
一句话结论:App Router 不只是"另一种路由方式",而是新的架构范式。遵循这 10 条规则,项目质量和性能会显著提升。
背景
Next.js 16 的 App Router 已经成熟,但很多团队仍在用 Pages Router 的思维写 App Router。结果:
- 性能没提升(仍在 hydrate 整个页面)
- JS bundle 仍很大
- SEO 仍不友好
下面 10 条规则是从 Vercel 团队和大型项目实战中提炼的核心原则。
规则 1:默认 Server Component,加 'use client' 才用 Client
// ❌ 反面:默认全部 'use client'
'use client';
export default function Page() {
const [count, setCount] = useState(0);
return <div onClick={() => setCount(count + 1)}>{count}</div>;
}
// ✅ 正面:默认 Server,只交互部分 Client
// app/page.tsx (Server)
export default async function Page() {
const data = await getData();
return (
<div>
<h1>{data.title}</h1>
{/* 只有这个按钮需要交互 */}
<LikeButton id={data.id} />
</div>
);
}
// components/LikeButton.tsx (Client)
'use client';
export function LikeButton({ id }: { id: string }) {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>{liked ? '❤️' : '🤍'}</button>;
}
效果:JS bundle 从 200KB 降到 5KB。
规则 2:数据获取放在 Server Component,不用 useEffect
// ❌ 反面:客户端获取
'use client';
export default function Products() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts);
}, []);
return <ProductList products={products} />;
}
// ✅ 正面:服务端直接获取
// app/products/page.tsx (Server)
export default async function Products() {
const products = await db.products.findMany();
return <ProductList products={products} />;
}
优势:
- 零 loading 状态
- 更安全(数据库不暴露给客户端)
- 更高效(避免二次请求)
规则 3:用 Promise.all 并行获取数据
// ❌ 反面:串行 await(瀑布流)
const user = await getUser();
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
// ✅ 正面:并行获取
const [user, posts, comments] = await Promise.all([
getUser(),
getPosts(),
getComments(),
]);
效果:首屏时间从 2.5s 降到 0.8s。
规则 4:用 Suspense 流式渲染慢的部分
// app/dashboard/page.tsx
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<ChartSkeleton />}>
<SlowChart /> {/* 即便慢也不阻塞其他 */}
</Suspense>
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
</div>
);
}
async function SlowChart() {
// 模拟慢查询
const data = await fetch('/api/heavy-stats', {
next: { revalidate: 60 }
});
return <Chart data={await data.json()} />;
}
效果:用户立即看到骨架屏 → 渐进填充,无需 loading spinner。
规则 5:路由组 (Route Groups) 组织代码,不用嵌套 URL
// 文件结构:
// app/
// (marketing)/
// page.tsx → /
// pricing/page.tsx → /pricing
// (app)/
// dashboard/page.tsx → /dashboard
// settings/page.tsx → /settings
// layout.tsx → 根布局
好处:
- 用 layout 区分"营销页"和"应用页"(不同布局)
- URL 保持简洁(不暴露
(marketing)/ (app)) - 代码组织更清晰
// app/(marketing)/layout.tsx
export default function MarketingLayout({ children }) {
return (
<div>
<Header />
<main className="container mx-auto">{children}</main>
<Footer />
</div>
);
}
// app/(app)/layout.tsx
export default function AppLayout({ children }) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
);
}
规则 6:用 Server Actions 替代 API routes(mutation 场景)
// ❌ 反面:写 API route + Client Component
// app/api/posts/route.ts
export async function POST(req: NextRequest) {
const data = await req.json();
await db.posts.create({ data });
return NextResponse.json({ success: true });
}
// components/NewPostForm.tsx
'use client';
export function NewPostForm() {
return (
<form onSubmit={async (e) => {
e.preventDefault();
await fetch('/api/posts', { method: 'POST', body: ... });
}}>
...
</form>
);
}
// ✅ 正面:Server Actions
// app/posts/actions.ts
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.posts.create({ data: { title } });
revalidatePath('/posts');
}
// components/NewPostForm.tsx
export function NewPostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">发布</button>
</form>
);
}
优势:少写 50% 代码,自动处理错误、重试、CSRF。
规则 7:用 generateMetadata 做动态 SEO
// app/products/[id]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata({
params
}: {
params: { id: string }
}): Promise<Metadata> {
const product = await db.products.findUnique({
where: { id: params.id }
});
return {
title: `${product.name} | 我的商店`,
description: product.description,
openGraph: {
images: [product.image],
},
};
}
export default async function ProductPage({
params
}: {
params: { id: string }
}) {
const product = await db.products.findUnique({
where: { id: params.id }
});
return <ProductDetail product={product} />;
}
优势:每个产品页都有独特的 SEO meta,无需 client-side。
规则 8:用 generateStaticParams 预渲染高频路由
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await db.posts.findMany({
where: { published: true },
select: { slug: true },
take: 100, // 预渲染最热的 100 篇
});
return posts.map(post => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
const post = await db.posts.findUnique({
where: { slug: params.slug }
});
return <Article post={post} />;
}
效果:热门文章首屏 0ms(直接 HTML),长尾文章 ISR(按需渲染)。
规则 9:用 Parallel Routes + Intercepting Routes 做模态
// 文件结构:
// app/
// @modal/
// (.)photos/[id]/page.tsx → 拦截 /photos/[id],作为模态
// photos/
// [id]/page.tsx → /photos/[id] 正常页面
// layout.tsx → 同时渲染 page 和 modal
// app/layout.tsx
export default function Layout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<>
{children}
{modal}
</>
);
}
效果:列表 → 点击照片 → 弹出模态(URL 仍是 /photos/123),刷新 = 完整页。
规则 10:用 revalidate 做 ISR,不用全量重建
// app/posts/page.tsx
export const revalidate = 60; // 每 60 秒重新验证
export default async function Posts() {
const posts = await db.posts.findMany();
return <PostList posts={posts} />;
}
// 按需重新验证
// app/posts/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function publishPost(id: string) {
await db.posts.update({
where: { id },
data: { published: true },
});
revalidatePath('/posts'); // 立即失效缓存
revalidatePath(`/posts/${id}`);
}
优势:内容更新即时生效,无需重新部署。
5 个常见错误
错误 1:在 Server Component 里用 hooks
// ❌ 报错
export default async function Page() {
const [count, setCount] = useState(0); // ❌
}
错误 2:Client Component 嵌套 Server Component
'use client';
import { ServerComponent } from './server'; // ❌
错误 3:把 'use client' 放在文件顶部,但实际只在小组件用
// ❌ 反面:整个页面 'use client'
'use client';
export default function Page() {
return (
<div>
<Header /> {/* 这部分本可 Server */}
<InteractiveForm /> {/* 只有这个需要 Client */}
</div>
);
}
// ✅ 正面:把 'use client' 下沉到交互组件
// app/page.tsx (Server)
export default function Page() {
return (
<div>
<Header />
<InteractiveForm /> {/* 这个文件自己 'use client' */}
</div>
);
}
错误 4:用 useEffect 获取数据
'use client';
export default function Page() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data').then(...); // ❌ 应该有 Server Component
}, []);
}
错误 5:忘记 add 'use server' to action
// app/actions.ts
export async function deleteUser(id: string) { // ❌ 缺 'use server'
await db.users.delete({ where: { id } });
}
性能优化清单
- [ ] 所有数据获取在 Server Component
- [ ] 默认无 'use client'
- [ ] 用 Promise.all 并行获取
- [ ] 用 Suspense 流式渲染
- [ ] 静态生成用 generateStaticParams
- [ ] ISR 用 revalidate 配置
- [ ] 图片用 next/image
- [ ] 字体用 next/font
- [ ] 第三方脚本用 next/script (策略:lazyOnload)
- [ ] 监控 Core Web Vitals
我的看法
App Router 是 Next.js 的未来,也是 RSC 落地最成熟的框架:
- 学习曲线:需要重新理解"边界"概念,但回报巨大
- 生态:所有 Next.js 插件都已支持
- Vercel 投入:未来 App Router 是主推方向
我的建议:
- 新项目:直接用 App Router
- 旧项目:渐进式迁移,先把"只读"页面转 RSC
- 状态复杂的页面:保留 Pages Router,不必强转
参考
本文规则基于 Vercel 官方推荐 + 多个生产项目实战,2026 年 6 月最新整理。
📚 同主题文章
Next.js SEO 实战:从技术 SEO 到 GEO(生成式引擎优化)2026
2026 年 SEO 新规则:GEO(生成式引擎优化)让 ChatGPT / Perplexity / Claude 引用你的内容。本文 Next.js 16 SEO + GEO 完整指南,含 6 大策略 + 4 个实战。
Motion + 动画设计实战:现代 Web 交互动效完整指南 2026
Motion(原 Framer Motion)是 2026 年最流行的 React 动画库。本文从 0 到生产级交互动效,含 4 个实战项目 + 性能优化 + AI 动画 + 设计令牌。
shadcn/ui + 设计系统 2026:现代 Web 产品的 UI 实战
shadcn/ui 是 2026 年最火的前端组件库 + 设计系统方案。本文从 0 到完整设计系统,含 5 个真实项目 + 主题定制 + 组件库扩展 + A11y。