Skip to Content

3.2 Next.js核心概念

Next.js 是 React 的全栈框架,让你能快速构建现代化的 Web 应用。它解决了传统 React 开发中的很多痛点。

学习重点:理解 Next.js 的核心概念,特别是文件系统路由、服务端渲染、静态生成等特性。

为什么选择 Next.js?

Next.js 的优势

特性传统 ReactNext.js说明
路由需要额外配置文件系统自动路由文件即路由
SEO客户端渲染,SEO差服务端渲染,SEO友好搜索引擎优化
性能首屏加载慢自动代码分割,性能好用户体验
部署配置复杂一键部署Vercel 等平台支持
API需要后端服务内置 API 路由全栈开发

AI 编程优势:Next.js 的约定优于配置理念,让 AI 更容易理解和生成符合规范的代码。

Next.js 项目结构

基础项目结构

my-nextjs-app/ ├── app/ # App Router (Next.js 13+) │ ├── globals.css # 全局样式 │ ├── layout.tsx # 根布局 │ ├── page.tsx # 首页 │ ├── loading.tsx # 加载组件 │ ├── error.tsx # 错误页面 │ └── about/ # 路由文件夹 │ └── page.tsx # /about 页面 ├── public/ # 静态资源 │ ├── images/ │ └── favicon.ico ├── components/ # 组件文件夹 ├── lib/ # 工具函数 ├── styles/ # 样式文件 ├── package.json └── next.config.js # Next.js 配置

App Router vs Pages Router

App Router (Next.js 13+)

特点

  • 基于文件系统的路由
  • 支持嵌套布局
  • 服务端组件默认
  • 并行路由和拦截路由

文件结构

app/ ├── layout.tsx # 根布局 ├── page.tsx # 首页 ├── loading.tsx # 加载状态 ├── error.tsx # 错误处理 ├── about/ │ ├── layout.tsx # about 布局 │ └── page.tsx # /about 页面 └── api/ └── users/ └── route.ts # /api/users API

Pages Router (传统)

特点

  • 基于文件的路由
  • 客户端组件默认
  • 需要手动配置布局
  • 成熟稳定,生态丰富

文件结构

pages/ ├── _app.tsx # 根组件 ├── _document.tsx # 文档组件 ├── index.tsx # 首页 ├── about.tsx # /about 页面 └── api/ └── users.ts # /api/users API

页面和路由

基础路由创建

创建步骤

  1. 创建文件
app/ ├── about/ │ └── page.tsx # 创建 about 页面 ├── contact/ │ └── page.tsx # 创建 contact 页面 └── blog/ └── [slug]/ └── page.tsx # 动态路由页面
  1. 编写页面组件
// app/about/page.tsx export default function AboutPage() { return ( <div> <h1>关于我们</h1> <p>这里是关于页面的内容...</p> </div> ); }
  1. 动态路由参数
// app/blog/[slug]/page.tsx export default function BlogPost({ params }: { params: { slug: string } }) { return ( <div> <h1>博客文章:{params.slug}</h1> <p>这里是文章内容...</p> </div> ); }

布局系统

// app/layout.tsx - 根布局 export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="zh-CN"> <body> <header> <nav>全局导航</nav> </header> <main>{children}</main> <footer>全局页脚</footer> </body> </html> ); } // app/about/layout.tsx - about 页面布局 export default function AboutLayout({ children, }: { children: React.ReactNode; }) { return ( <section> <aside>关于页面侧边栏</aside> <article>{children}</article> </section> ); }

数据获取方式

服务端组件 vs 客户端组件

服务端组件

特点

  • 在服务器端渲染
  • 直接访问数据库
  • 不能使用交互功能
  • 性能更好
// 默认为服务端组件 async function getUserData() { const res = await fetch('https://api.example.com/users'); return res.json(); } export default async function UserList() { const users = await getUserData(); return ( <div> <h1>用户列表</h1> {users.map(user => ( <div key={user.id}> <h3>{user.name}</h3> <p>{user.email}</p> </div> ))} </div> ); }

客户端组件

特点

  • 在浏览器端运行
  • 支持交互功能
  • 可以使用 hooks
  • 需要 ‘use client’ 指令
'use client'; // 声明为客户端组件 import { useState, useEffect } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>当前计数:{count}</p> <button onClick={() => setCount(count + 1)}> 增加 </button> </div> ); }

数据获取方法

// 静态生成 - 构建时获取数据 async function getStaticPosts() { const posts = await fetch('https://api.example.com/posts'); return posts.json(); } export default async function BlogPage() { const posts = await getStaticPosts(); return ( <div> <h1>博客列表</h1> {posts.map(post => ( <article key={post.id}> <h2>{post.title}</h2> <p>{post.excerpt}</p> </article> ))} </div> ); } // 动态渲染 - 请求时获取数据 async function getDynamicPost(id: string) { const res = await fetch(`https://api.example.com/posts/${id}`, { cache: 'no-store' // 禁用缓存 }); return res.json(); } export default async function PostPage({ params }: { params: { id: string } }) { const post = await getDynamicPost(params.id); return ( <article> <h1>{post.title}</h1> <div dangerouslySetInnerHTML={{ __html: post.content }} /> </article> ); }

API 路由

创建 API 端点

// app/api/users/route.ts import { NextRequest, NextResponse } from 'next/server'; // GET 请求处理 export async function GET(request: NextRequest) { try { // 从数据库获取用户数据 const users = [ { id: 1, name: '张三', email: '[email protected]' }, { id: 2, name: '李四', email: '[email protected]' } ]; return NextResponse.json({ users }); } catch (error) { return NextResponse.json( { error: '获取用户失败' }, { status: 500 } ); } } // POST 请求处理 export async function POST(request: NextRequest) { try { const body = await request.json(); const { name, email } = body; // 验证输入 if (!name || !email) { return NextResponse.json( { error: '姓名和邮箱不能为空' }, { status: 400 } ); } // 创建新用户(示例) const newUser = { id: Date.now(), name, email, createdAt: new Date().toISOString() }; return NextResponse.json( { message: '用户创建成功', user: newUser }, { status: 201 } ); } catch (error) { return NextResponse.json( { error: '创建用户失败' }, { status: 500 } ); } }

动态 API 路由

// app/api/users/[id]/route.ts import { NextRequest, NextResponse } from 'next/server'; export async function GET( request: NextRequest, { params }: { params: { id: string } } ) { try { const userId = params.id; // 从数据库获取特定用户 const user = { id: userId, name: '用户' + userId }; if (!user) { return NextResponse.json( { error: '用户不存在' }, { status: 404 } ); } return NextResponse.json({ user }); } catch (error) { return NextResponse.json( { error: '获取用户失败' }, { status: 500 } ); } }

样式解决方案

全局样式

/* app/globals.css */ @tailwind base; @tailwind components; @tailwind utilities; :root { --primary-color: #3b82f6; --secondary-color: #64748b; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; }

Tailwind CSS 集成

// 安装 Tailwind CSS // npm install -D tailwindcss postcss autoprefixer // npx tailwindcss init -p // tailwind.config.js /** @type {import('tailwindcss').Config} */ module.exports = { content: [ './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { colors: { primary: '#3b82f6', }, }, }, plugins: [], }; // 在组件中使用 export default function HomePage() { return ( <div className="min-h-screen bg-gray-50"> <header className="bg-white shadow-sm"> <nav className="container mx-auto px-4 py-4"> <h1 className="text-2xl font-bold text-primary"> 我的网站 </h1> </nav> </header> <main className="container mx-auto px-4 py-8"> <section className="text-center"> <h2 className="text-4xl font-bold mb-4"> 欢迎来到我的网站 </h2> <p className="text-gray-600 mb-8"> 这里是网站的主要内容 </p> <button className="bg-primary text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition-colors"> 开始使用 </button> </section> </main> </div> ); }

CSS Modules

// components/Button.module.css .button { padding: 12px 24px; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s ease; } .primary { background-color: #3b82f6; color: white; } .primary:hover { background-color: #2563eb; } .secondary { background-color: #f3f4f6; color: #374151; } .secondary:hover { background-color: #e5e7eb; } // components/Button.tsx import styles from './Button.module.css'; interface ButtonProps { variant?: 'primary' | 'secondary'; children: React.ReactNode; onClick?: () => void; } export default function Button({ variant = 'primary', children, onClick }: ButtonProps) { return ( <button className={`${styles.button} ${styles[variant]}`} onClick={onClick} > {children} </button> ); }

性能优化

代码分割

// 自动代码分割 import dynamic from 'next/dynamic'; // 动态导入组件 const DynamicComponent = dynamic(() => import('../components/HeavyComponent'), { loading: () => <p>加载中...</p>, ssr: false // 仅在客户端渲染 }); export default function HomePage() { return ( <div> <h1>首页</h1> <DynamicComponent /> </div> ); } // 按需导入 function HomePage() { const [showModal, setShowModal] = useState(false); const Modal = useMemo(() => dynamic(() => import('../components/Modal')), [] ); return ( <div> <h1>首页</h1> <button onClick={() => setShowModal(true)}> 打开模态框 </button> {showModal && <Modal />} </div> ); }

图片优化

import Image from 'next/image'; export default function ImageExample() { return ( <div> {/* 优化的图片 */} <Image src="/hero-image.jpg" alt="Hero 图片" width={800} height={600} priority // 重要图片优先加载 placeholder="blur" // 模糊占位符 className="rounded-lg" /> {/* 响应式图片 */} <Image src="/responsive-image.jpg" alt="响应式图片" fill sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" style={{ objectFit: 'cover' }} className="rounded-lg" /> </div> ); }

部署配置

环境变量

# .env.local - 本地环境变量 DATABASE_URL="postgresql://username:password@localhost:5432/mydb" NEXTAUTH_SECRET="your-secret-key" NEXTAUTH_URL="http://localhost:3000" # .env.production - 生产环境变量 DATABASE_URL="postgresql://username:password@host:5432/productiondb" NEXTAUTH_SECRET="production-secret" NEXTAUTH_URL="https://your-domain.com"

构建配置

// next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { experimental: { appDir: true, // 启用 App Router }, images: { domains: ['example.com', 'cdn.example.com'], // 允许的图片域名 }, env: { CUSTOM_KEY: process.env.CUSTOM_KEY, }, async redirects() { return [ { source: '/old-path', destination: '/new-path', permanent: true, }, ]; }, async rewrites() { return [ { source: '/blog/:slug', destination: '/posts/:slug', }, ]; }, }; module.exports = nextConfig;

AI 编程中的 Next.js

AI 生成 Next.js 代码的技巧

你是一位 Next.js 专家,请帮我创建一个博客应用: 项目要求: 1. 使用 Next.js 13+ App Router 2. 集成 Tailwind CSS 3. 包含文章列表和详情页面 4. 支持文章搜索功能 5. 响应式设计 6. 优化 SEO 和性能 技术栈: - Next.js 13+ with TypeScript - Tailwind CSS - 服务端组件优先 - 静态生成 + 增量静态再生 请提供: 1. 完整的项目结构 2. 核心页面代码 3. API 路由实现 4. 部署配置

下一步:掌握了 Next.js 基础后,继续学习 React组件与JSX,深入了解 React 的核心概念。

Last updated on