要点
App Router 用文件组织路由。page 定义页面入口,layout 提供共享外壳;组件函数返回的是界面描述。
核心技能掌握
- 掌握 Next.js 16项目创建和初始化流程
- 理解 App Router 的基本概念和文件约定
- 创建和管理页面路由和布局
- 掌握基础组件开发和页面渲染
- 理解开发服务器的工作原理
实践能力培养
- 独立创建 Next.js 项目
- 编写基础页面和组件
- 配置路由和导航
- 使用开发工具进行调试
- 部署第一个 Next.js 应用
概述
Next.js 16 是基于 React 19 的全栈框架,提供了强大的开发体验和生产性能。本教程将带领你从零开始创建第一个 Next.js 应用,学习核心概念和开发流程。
项目创建流程
第一步:环境准备
# 检查Node.js版本 (需要Node.js 20.9+)
node --version
1. 检查npm版本
npm --version
1. 创建项目目录
mkdir my-first-nextjs-app
cd my-first-nextjs-app
第二步:项目初始化
# 使用create-next-app创建项目
npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
1. 或者手动回答配置问题
npx create-next-app@latest
1. 项目配置选项:
1. Would you like to use TypeScript? … Yes
1. Would you like to use ESLint? … Yes
1. Would you like to use Tailwind CSS? … Yes
1. Would you like to use 'src/' directory? … Yes
1. Would you like to use App Router? … Yes
1. Would you like to customize the default import alias? … @/*
第三步:项目结构理解
my-first-nextjs-app/
├── src/
│ ├── app/
│ │ ├── layout.tsx # 根布局
│ │ ├── page.tsx # 首页
│ │ ├── globals.css # 全局样式
│ │ └── favicon.ico # 网站图标
│ ├── components/ # 组件目录
│ └── lib/ # 工具库
├── public/ # 静态资源
├── package.json # 依赖配置
├── next.config.js # Next.js配置
├── tsconfig.json # TypeScript配置
├── tailwind.config.ts # Tailwind配置
└── README.md # 项目说明
理解 App Router 架构
根布局 (layout.tsx)
// src/app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: '我的第一个Next.js应用',
description: '学习Next.js 16开发',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="zh-CN">
<body className={inter.className}>
<div className="min-h-screen bg-gray-50">
<header className="bg-blue-600 text-white p-4">
<h1 className="text-2xl font-bold">我的应用</h1>
</header>
<main className="container mx-auto p-4">
{children}
</main>
<footer className="bg-gray-800 text-white p-4 text-center">
<p>© 2025 我的Next.js应用</p>
</footer>
</div>
</body>
</html>
)
}
首页 (page.tsx)
// src/app/page.tsx
import Image from 'next/image'
export default function HomePage() {
return (
<div className="space-y-8">
<section className="text-center py-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
欢迎来到Next.js 16
</h1>
<p className="text-xl text-gray-600 mb-8">
这是你的第一个Next.js应用
</p>
<div className="space-x-4">
<button className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition">
开始学习
</button>
<button className="border border-blue-600 text-blue-600 px-6 py-3 rounded-lg hover:bg-blue-50 transition">
查看示例
</button>
</div>
</section>
<section className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2">🚀 快速开发</h3>
<p className="text-gray-600">
Next.js提供强大的开发工具和零配置体验
</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2">⚡ 高性能</h3>
<p className="text-gray-600">
自动优化和现代化的渲染策略
</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<h3 className="text-xl font-semibold mb-2">🎯 SEO友好</h3>
<p className="text-gray-600">
内置SEO优化和搜索引擎友好特性
</p>
</div>
</section>
</div>
)
}
创建路由页面
关于页面
// src/app/about/page.tsx
export default function AboutPage() {
return (
<div className="max-w-4xl mx-auto">
<h1 className="text-3xl font-bold text-gray-900 mb-6">
关于我们
</h1>
<div className="prose prose-lg">
<p className="text-gray-600 mb-4">
这是使用Next.js 16构建的第一个应用。在这个项目中,你将学习:
</p>
<ul className="list-disc pl-6 space-y-2">
<li>App Router的基本概念和使用</li>
<li>页面和布局的创建方法</li>
<li>组件开发和状态管理</li>
<li>样式系统和UI设计</li>
<li>数据获取和API集成</li>
</ul>
</div>
<div className="mt-8 p-6 bg-blue-50 rounded-lg">
<h2 className="text-xl font-semibold text-blue-900 mb-4">
技术栈
</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl mb-2">⚛️</div>
<div className="text-sm">React 19</div>
</div>
<div className="text-center">
<div className="text-2xl mb-2">🚀</div>
<div className="text-sm">Next.js 16</div>
</div>
<div className="text-center">
<div className="text-2xl mb-2">📘</div>
<div className="text-sm">TypeScript</div>
</div>
<div className="text-center">
<div className="text-2xl mb-2">🎨</div>
<div className="text-sm">Tailwind CSS</div>
</div>
</div>
</div>
</div>
)
}
联系页面
// src/app/contact/page.tsx
'use client' // 使用客户端组件
import { useState } from 'react'
export default function ContactPage() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
})
const [isSubmitting, setIsSubmitting] = useState(false)
const [submitted, setSubmitted] = useState(false)
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsSubmitting(true)
// 模拟表单提交
await new Promise(resolve => setTimeout(resolve, 1000))
setSubmitted(true)
setIsSubmitting(false)
setFormData({ name: '', email: '', message: '' })
}
if (submitted) {
return (
<div className="max-w-2xl mx-auto text-center">
<div className="bg-green-50 border border-green-200 rounded-lg p-8">
<h2 className="text-2xl font-bold text-green-800 mb-4">
✅ 提交成功!
</h2>
<p className="text-green-700">
感谢你的留言,我们会尽快回复你。
</p>
<button
onClick={() => setSubmitted(false)}
className="mt-4 bg-green-600 text-white px-6 py-2 rounded-lg hover:bg-green-700"
>
发送新消息
</button>
</div>
</div>
)
}
return (
<div className="max-w-2xl mx-auto">
<h1 className="text-3xl font-bold text-gray-900 mb-6">
联系我们
</h1>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-2">
姓名
</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
邮箱
</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-2">
留言
</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
required
rows={4}
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-blue-600 text-white py-3 rounded-lg hover:bg-blue-700 disabled:bg-gray-400 transition"
>
{isSubmitting ? '发送中...' : '发送消息'}
</button>
</form>
</div>
)
}
创建可复用组件
导航组件
// src/components/Navigation.tsx
import Link from 'next/link'
export default function Navigation() {
return (
<nav className="bg-white shadow-md">
<div className="container mx-auto px-4">
<div className="flex justify-between items-center h-16">
<Link href="/" className="text-xl font-bold text-blue-600">
MyApp
</Link>
<div className="flex space-x-8">
<Link
href="/"
className="text-gray-700 hover:text-blue-600 transition"
>
首页
</Link>
<Link
href="/about"
className="text-gray-700 hover:text-blue-600 transition"
>
关于
</Link>
<Link
href="/contact"
className="text-gray-700 hover:text-blue-600 transition"
>
联系
</Link>
</div>
</div>
</div>
</nav>
)
}
卡片组件
// src/components/Card.tsx
import { ReactNode } from 'react'
interface CardProps {
children: ReactNode
className?: string
}
export default function Card({ children, className = '' }: CardProps) {
return (
<div className={`bg-white rounded-lg shadow-md p-6 ${className}`}>
{children}
</div>
)
}
interface CardHeaderProps {
children: ReactNode
className?: string
}
export function CardHeader({ children, className = '' }: CardHeaderProps) {
return (
<div className={`mb-4 ${className}`}>
{children}
</div>
)
}
interface CardContentProps {
children: ReactNode
className?: string
}
export function CardContent({ children, className = '' }: CardContentProps) {
return (
<div className={className}>
{children}
</div>
)
}
开发和调试
启动开发服务器
# 启动开发服务器
npm run dev
1. 或者使用yarn
yarn dev
1. 或者使用pnpm
pnpm dev
开发服务器将在 http://localhost:3000 启动,支持: – 热重载 – 代码修改后自动刷新 – 错误提示 – 详细的错误信息和修复建议 – Fast Refresh – 保持组件状态的快速刷新
常用开发命令
# 开发模式
npm run dev
1. 构建生产版本
npm run build
1. 启动生产服务器
npm start
1. 代码检查
npm run lint
1. 类型检查
npm run type-check
项目配置优化
Next.js 配置
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// Turbopack 配置(Next.js 16 为顶层键,不再是 experimental.turbo)
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
// 图片优化
images: {
formats: ['image/webp', 'image/avif'],
remotePatterns: [{ protocol: 'https', hostname: 'example.com' }], // 允许的图片域名
},
// 重定向配置
async redirects() {
return [
{
source: '/home',
destination: '/',
permanent: true,
},
]
},
// 头部配置
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
],
},
]
},
}
module.exports = nextConfig
TypeScript 配置
// tsconfig.json
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "es6"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
部署第一个应用
Vercel 部署(推荐)
# 安装Vercel CLI
npm i -g vercel
1. 登录Vercel
vercel login
1. 部署项目
vercel
1. 部署生产环境
vercel --prod
构建和部署
# 构建项目
npm run build
1. 检查构建结果
npm start
1. 部署到其他平台
1. 将 .next 目录和 public 目录上传到服务器
实践练习
练习1:创建个人介绍页面
- 创建
/profile路由 - 展示个人信息、技能和经历
- 使用组件化思想组织页面
练习2:作品集页面
- 创建
/portfolio路由 - 展示项目作品
- 实现筛选和搜索功能
练习3:博客页面
- 创建
/blog路由 - 展示文章列表
- 实现文章详情页面
总结
通过本教程,你已经学会了:
- 项目创建: 使用 create-next-app 创建 Next.js 项目
- 文件结构: 理解 App Router 的文件约定
- 路由系统: 创建和管理页面路由
- 组件开发: 编写可复用的 React 组件
- 样式系统: 使用 Tailwind CSS 进行样式设计
- 交互功能: 实现表单和用户交互
- 开发调试: 使用开发工具进行调试
- 项目部署: 将应用部署到生产环境
下一步学习
- 深入学习数据获取和 API 集成
- 掌握状态管理的高级技巧
- 学习性能优化和 SEO 策略
- 探索测试和质量保证
- 了解部署和运维最佳实践
第一个应用的目标是建立可观察的开发闭环:能启动、能找到页面入口、能区分服务端和客户端交互、能从报错定位到文件,而不是只看到默认欢迎页。
文档交叉引用
相关文档
参考章节
- 📖 [本模块其他章节]: TypeScript 集成配置 | 布局和路由设计
- 📖 [Knowledge Points 快速参考]: React 语法速查 | Next.js API 参考
总结
第一个应用应形成的闭环
- 创建结果可复现:记录所用包管理器和命令,启动开发服务器后访问本地 URL;若端口被占用、依赖未安装或运行时版本不兼容,先记录完整报错而不是随意修改配置文件。
- 目录与 URL 能互相解释:新建一个静态页面和一个动态路径,能从地址栏找到对应目录,也能说明
layout与page分别承担共享结构和页面内容。 - 客户端边界最小化:默认组件保持服务端渲染;只有需要事件、浏览器 API 或本地状态的子组件使用客户端边界。不要把整个页面标记为客户端以逃避组件边界的理解。
- 导航保留用户语义:链接使用真实目的地和可访问名称;导航后能确认 URL、标题或主要内容变化,浏览器后退也回到预期状态。
- 报错能定位与缩小:主动制造一个语法、导入或渲染错误,使用终端和浏览器错误覆盖层找到文件与行号;恢复时只改与原因有关的最小部分。
学习成果验收
- [ ] 从空终端运行创建、安装和开发命令,保存运行时版本、启动 URL 与退出或错误输出。
- [ ] 新增一个
/about页面和一个带参数的详情路径,直接访问、从链接进入和浏览器后退都得到可预期结果。 - [ ] 把一个点击计数器拆为最小客户端组件,确认父页面仍可作为服务端组件读取或组织内容。
- [ ] 只用键盘通过导航链接到达新页面,确认焦点与主要内容可感知地变化。
- [ ] 故意拼错一次导入或 JSX,记录定位信息;修复后确认页面恢复且没有通过隐藏错误或关闭检查来解决。
贡献与反馈
内容改进
如果你发现本文档有改进空间,欢迎: – 🐛 报告问题: 在 Issues 中提出具体问题 – 💡 建议改进: 提出修改建议和补充内容 – 📝 参与贡献: 提交 PR 完善文档内容
学习反馈
分享你的学习体验: – ✅ 有用内容: 哪些部分对你最有帮助 – ❓ 疑问点: 哪些内容需要进一步澄清 – 🎯 建议: 希望增加什么内容
文档状态: ✅ 已完成最后更新: 2026年9月版本: v1.0.0