第一个 Next.js 应用

要点

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>&copy; 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 路由
  • 展示文章列表
  • 实现文章详情页面

总结

通过本教程,你已经学会了:

  1. 项目创建: 使用 create-next-app 创建 Next.js 项目
  2. 文件结构: 理解 App Router 的文件约定
  3. 路由系统: 创建和管理页面路由
  4. 组件开发: 编写可复用的 React 组件
  5. 样式系统: 使用 Tailwind CSS 进行样式设计
  6. 交互功能: 实现表单和用户交互
  7. 开发调试: 使用开发工具进行调试
  8. 项目部署: 将应用部署到生产环境

下一步学习

  • 深入学习数据获取和 API 集成
  • 掌握状态管理的高级技巧
  • 学习性能优化和 SEO 策略
  • 探索测试和质量保证
  • 了解部署和运维最佳实践

第一个应用的目标是建立可观察的开发闭环:能启动、能找到页面入口、能区分服务端和客户端交互、能从报错定位到文件,而不是只看到默认欢迎页。

文档交叉引用

相关文档

参考章节

  • 📖 [本模块其他章节]: TypeScript 集成配置 | 布局和路由设计
  • 📖 [Knowledge Points 快速参考]: React 语法速查 | Next.js API 参考

总结

第一个应用应形成的闭环

  1. 创建结果可复现:记录所用包管理器和命令,启动开发服务器后访问本地 URL;若端口被占用、依赖未安装或运行时版本不兼容,先记录完整报错而不是随意修改配置文件。
  2. 目录与 URL 能互相解释:新建一个静态页面和一个动态路径,能从地址栏找到对应目录,也能说明 layout 与 page 分别承担共享结构和页面内容。
  3. 客户端边界最小化:默认组件保持服务端渲染;只有需要事件、浏览器 API 或本地状态的子组件使用客户端边界。不要把整个页面标记为客户端以逃避组件边界的理解。
  4. 导航保留用户语义:链接使用真实目的地和可访问名称;导航后能确认 URL、标题或主要内容变化,浏览器后退也回到预期状态。
  5. 报错能定位与缩小:主动制造一个语法、导入或渲染错误,使用终端和浏览器错误覆盖层找到文件与行号;恢复时只改与原因有关的最小部分。

学习成果验收

  • [ ] 从空终端运行创建、安装和开发命令,保存运行时版本、启动 URL 与退出或错误输出。
  • [ ] 新增一个 /about 页面和一个带参数的详情路径,直接访问、从链接进入和浏览器后退都得到可预期结果。
  • [ ] 把一个点击计数器拆为最小客户端组件,确认父页面仍可作为服务端组件读取或组织内容。
  • [ ] 只用键盘通过导航链接到达新页面,确认焦点与主要内容可感知地变化。
  • [ ] 故意拼错一次导入或 JSX,记录定位信息;修复后确认页面恢复且没有通过隐藏错误或关闭检查来解决。

贡献与反馈

内容改进

如果你发现本文档有改进空间,欢迎: – 🐛 报告问题: 在 Issues 中提出具体问题 – 💡 建议改进: 提出修改建议和补充内容 – 📝 参与贡献: 提交 PR 完善文档内容

学习反馈

分享你的学习体验: – ✅ 有用内容: 哪些部分对你最有帮助 – ❓ 疑问点: 哪些内容需要进一步澄清 – 🎯 建议: 希望增加什么内容


文档状态: ✅ 已完成最后更新: 2026年9月版本: v1.0.0

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇