Skip to Content

3.2 Next.js 核心概念(极简版)

在这一节,我们只做一件事:
把「AI 生成的网页」升级成一个真正能多页跳转、方便部署的 Next.js 小站。

可以把 Next.js 理解成:
在普通网页外面再包一层「网站框架」,帮你处理路由、性能和 SEO。
你不需要懂所有细节,只需要知道文件放哪、怎么跑起来。

先弄清 3 个概念就够了

概念像什么你需要知道的
页面(page.tsx)每一页内容一个文件 = 一个网址
布局(layout.tsx)网站的外壳头部、脚部在这里统一管理
路由(地址)菜单/房间号文件夹路径 = 浏览器地址

其他像「服务端组件」、「静态生成」、「缓存策略」等,此时都可以先不管。

用 AI 帮你创建一个 Next.js 项目

第 1 步: 让 AI 给出命令

在 Cursor / 你喜欢的 AI 里输入:

我想在本地创建一个 Next.js 13+ 的新项目,使用 TypeScript。 请一步一步告诉我: 1. 需要在终端里输入哪些命令 2. 每个命令是干什么的 3. 项目创建好之后,我怎么在浏览器里打开它

通常会得到类似步骤:

npx create-next-app@latest my-app --ts cd my-app npm run dev

执行完后,在浏览器访问 http://localhost:3000 即可看到默认页面。

第 2 步: 认识两个关键文件

在项目里找到:

  • app/layout.tsx — 网站外壳
  • app/page.tsx — 首页内容

暂时你只需要知道:

  • layout.tsx → 改整站的标题、全局样式
  • page.tsx → 改首页看到的内容

第 3 步: 用 AI 改造首页

复制当前 app/page.tsx 的内容,发给 AI,说:

这是我 Next.js 项目的首页代码,请帮我改成一个简单的"个人介绍"页面: - 顶部有一个大标题"我是 XXX" - 中间用两三句话介绍自己 - 下面有一个按钮,点击后跳转到 /contact 页面 请直接给我完整的 page.tsx 代码。

把返回的代码粘回 app/page.tsx,保存,刷新浏览器查看效果。

最简单的多页面导航

Next.js 最爽的一点是: 文件夹 = 路由

  • app/page.tsx/
  • app/about/page.tsx/about
  • app/contact/page.tsx/contact

你可以这样让 AI 帮你生成一个新页面:

在我的 Next.js 项目中,请帮我创建一个 `app/contact/page.tsx` 文件, 内容是一个简单的联系页面,包含: - 一段提示语 - 一个邮箱地址 - 一个返回首页的按钮 请给我完整代码,我会新建文件后粘贴进去。
🧭

你需要掌握的路由知识,只有两点:

  1. 新建文件夹 + page.tsx = 新的路由
  2. 用链接组件跳转: 让 AI 帮你在首页加上到 /about/contact 的导航

这一节做到什么程度就可以继续?

建议达成下面 3 条再往下:

  • 能在本地创建一个 Next.js 项目并打开首页
  • 能改 app/page.tsx 的文案和简单布局
  • 能在 app 里多建 1–2 个页面并互相跳转

如果其中某一步做不出来,就把当前文件/报错丢给 AI,用一句话概括自己的目标:

我在做一个 Next.js 项目,希望有首页 / 关于 / 联系三页, 现在卡在这一步:[描述具体问题],请一步一步教我怎么改。

下一节: 综合项目实战 – 用刚刚搭好的 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 基础后,直接进入 3.3 综合项目实战,跟着一步步做出你的第一个小网站。

Last updated on