第 2 章:快速上手 — 创建你的第一个 Next.js 项目

从零开始创建一个 Next.js 15 项目,掌握项目初始化、TypeScript 配置、目录结构、开发环境、第一个页面、第一个 API、TailwindCSS 集成,以及部署到 Vercel 的完整流程。

本章是实战章节。读完本章,你将完成一个包含页面、API、TailwindCSS 样式的完整 Next.js 项目,并成功部署到 Vercel。


2.1 使用 create-next-app 初始化项目

前置要求

在开始之前,确保你的开发环境满足以下要求:

工具最低版本检查命令
Node.js18.17 或更高node -v
npm / pnpm / yarn最新版npm -v
Git最新版git --version
代码编辑器VS Code(推荐)-

推荐使用 pnpm:pnpm 比 npm 快 2-3 倍,磁盘占用更少,monorepo 支持更好。安装命令:npm install -g pnpm

初始化项目

使用官方脚手架 create-next-app 创建项目:

npx create-next-app@latest my-nextjs-app

或使用 pnpm(推荐):

pnpm create next-app my-nextjs-app

交互式配置选项:

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes

推荐选择

选项选择原因
TypeScript✅ Yes类型安全,减少运行时错误
ESLint✅ Yes代码质量检查,集成 Next.js 规则
Tailwind CSS✅ Yes原子化 CSS,快速开发 UI
src/ directory❌ No简化目录结构(个人偏好)
App Router✅ Yes未来标准,RSC + Server Actions
Import alias✅ Yes,保持 @/*方便导入,如 @/components/Button

项目初始化完成

cd my-nextjs-app

查看生成的目录结构:

my-nextjs-app/
├── app/
│   ├── favicon.ico
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── public/
│   ├── file.svg
│   ├── globe.svg
│   ├── next.svg
│   ├── vercel.svg
│   └── window.svg
├── .gitignore
├── eslint.config.mjs
├── next.config.ts
├── package.json
├── postcss.config.mjs
├── README.md
├── tailwind.config.ts
└── tsconfig.json

2.2 TypeScript 配置与标准目录结构解析

TypeScript 配置

打开 tsconfig.json,查看默认配置:

{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "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"
      }
    ],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

关键配置解读

配置项说明
stricttrue启用严格模式,强制类型检查
moduleResolutionbundler使用打包器解析模块(Next.js 推荐)
paths@/*: ["./*"]路径别名,@/components/Button./components/Button
plugins[{ name: "next" }]Next.js TypeScript 插件,提供类型提示

推荐补充配置

{
  "compilerOptions": {
    // ... 其他配置
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  }
}

标准目录结构解析

Next.js 项目的核心目录:

app/ — 应用目录(App Router)

app/
├── layout.tsx      # 根布局(全局 Header、Footer)
├── page.tsx        # 根页面(/)
├── globals.css     # 全局样式
└── favicon.ico     # 网站图标
  • layout.tsx:布局组件,包裹所有子路由页面
  • page.tsx:页面组件,每个路由段一个 page.tsx
  • globals.css:全局 CSS(Tailwind 指令在这里)

public/ — 静态资源

public/
├── file.svg
├── globe.svg
├── next.svg
├── vercel.svg
└── window.svg
  • 存放图片、字体、robots.txt、sitemap.xml 等静态文件
  • 引用时不需要 /public 前缀:<img src="/next.svg" />

next.config.ts — Next.js 配置

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* 配置选项 */
};

export default nextConfig;

常用配置:

const nextConfig: NextConfig = {
  // 启用实验性功能
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',
    },
  },
  // 图片优化配置
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
      },
    ],
  },
};

package.json — 依赖管理

{
  "name": "my-nextjs-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "next": "^15.0.0"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "eslint": "^9",
    "eslint-config-next": "^15.0.0"
  }
}

核心脚本

命令作用
pnpm dev启动开发服务器(http://localhost:3000)
pnpm build构建生产版本
pnpm start启动生产服务器
pnpm lint运行 ESLint 检查

2.3 开发模式、HMR 热更新与调试工具配置

启动开发服务器

pnpm dev

输出:

▲ Next.js 15.0.0
- Local:        http://localhost:3000
- Network:      http://192.168.1.100:3000

✓ Starting...
✓ Ready in 2.3s

打开浏览器访问 http://localhost:3000,你将看到 Next.js 默认首页。

HMR(Hot Module Replacement)热更新

修改 app/page.tsx

// app/page.tsx
export default function Home() {
  return (
    <div className="p-8">
      <h1 className="text-3xl font-bold">Hello, Next.js!</h1>
      <p className="mt-4 text-gray-600">这是我的第一个 Next.js 项目。</p>
    </div>
  );
}

保存文件后,浏览器自动刷新,无需手动刷新页面。这就是 HMR(热模块替换)的魔力。

HMR 的工作原理

  1. 你修改了 page.tsx
  2. Next.js 检测到文件变化
  3. 只重新编译 page.tsx,不重新编译整个项目
  4. 通过 WebSocket 将新代码推送到浏览器
  5. 浏览器替换对应模块,保留组件状态

HMR 的优势

  • 速度快:只编译变化的文件,增量编译
  • 状态保留:组件状态(如表单输入)不会丢失
  • 即时反馈:修改后立即看到效果

调试工具配置

1. VS Code 推荐插件

插件用途
ES7+ React/Redux/React-Native snippetsReact 代码片段
Tailwind CSS IntelliSenseTailwind 类名自动补全
Prettier - Code formatter代码格式化
Error Lens错误信息直接显示在代码行
Thunder ClientAPI 测试(Postman 替代)

2. Chrome DevTools

Next.js 开发模式下,Chrome DevTools 提供:

  • Elements:查看 DOM 结构、Tailwind 类名
  • Console:查看 console.log 输出、错误信息
  • Network:查看 API 请求、资源加载
  • Lighthouse:性能分析、SEO 检查
  • React DevTools:查看组件树、Props、Hooks(需安装扩展)

安装 React DevTools:

Chrome Web Store → 搜索 "React Developer Tools" → 安装

3. 调试 Server Components

Server Components 在服务端执行,无法使用 Chrome DevTools 调试。推荐使用 console.log

// app/page.tsx
async function getData() {
  console.log('[Server] Fetching data...');
  const res = await fetch('https://api.example.com/posts');
  return res.json();
}

export default async function Home() {
  console.log('[Server] Rendering Home component');
  const data = await getData();
  return <div>{/* ... */}</div>;
}

查看终端输出:

[Server] Rendering Home component
[Server] Fetching data...

4. 调试 Client Components

Client Components 在浏览器执行,可以使用 Chrome DevTools:

// app/counter.tsx
"use client";

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  console.log('[Client] Counter rendered, count:', count);

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

打开 Chrome DevTools → Console,查看输出。


2.4 编写第一个页面:app/page.tsx

默认首页

打开 app/page.tsx,查看默认代码:

import Image from "next/image";

export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />
        <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
          <li className="mb-2">
            Get started by editing{" "}
            <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
              app/page.tsx
            </code>
            .
          </li>
          <li>Save and see your changes instantly.</li>
        </ol>

        <div className="flex gap-4 items-center flex-col sm:flex-row">
          <a
            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            <Image
              className="dark:invert"
              src="/vercel.svg"
              alt="Vercel logomark"
              width={20}
              height={20}
            />
            Deploy now
          </a>
          <a
            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            Read our docs
          </a>
        </div>
      </main>
      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/file.svg"
            alt="File icon"
            width={16}
            height={16}
          />
          Learn
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/window.svg"
            alt="Window icon"
            width={16}
            height={16}
          />
          Examples
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/globe.svg"
            alt="Globe icon"
            width={16}
            height={16}
          />
          Go to nextjs.org 
        </a>
      </footer>
    </div>
  );
}

简化首页

app/page.tsx 替换为更简洁的版本:

// app/page.tsx
export default function Home() {
  return (
    <main className="min-h-screen p-8 bg-gradient-to-br from-blue-50 to-indigo-100">
      <div className="max-w-4xl mx-auto">
        <h1 className="text-5xl font-bold text-gray-900 mb-4">
          Welcome to Next.js 15
        </h1>
        <p className="text-xl text-gray-600 mb-8">
          这是你的第一个 Next.js 项目。让我们开始构建吧!
        </p>
        
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div className="bg-white p-6 rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold mb-2">📚 学习</h2>
            <p className="text-gray-600">
              阅读 Next.js 官方文档,学习 App RouterServer Components 等核心概念。
            </p>
          </div>
          
          <div className="bg-white p-6 rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold mb-2">🚀 构建</h2>
            <p className="text-gray-600">
              开始构建你的第一个全栈应用,包括页面、API、数据库。
            </p>
          </div>
          
          <div className="bg-white p-6 rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold mb-2">🎨 设计</h2>
            <p className="text-gray-600">
              使用 TailwindCSS 快速构建美观的 UI 组件。
            </p>
          </div>
          
          <div className="bg-white p-6 rounded-lg shadow-md">
            <h2 className="text-2xl font-semibold mb-2">🌍 部署</h2>
            <p className="text-gray-600">
              将应用部署到 Vercel,分享给全世界。
            </p>
          </div>
        </div>
      </div>
    </main>
  );
}

保存后,浏览器自动刷新,显示新的首页。

创建子页面

创建 app/about/page.tsx

// app/about/page.tsx
export default function About() {
  return (
    <main className="min-h-screen p-8 bg-gray-50">
      <div className="max-w-4xl mx-auto">
        <h1 className="text-4xl font-bold text-gray-900 mb-4">
          关于我们
        </h1>
        <p className="text-lg text-gray-600 mb-6">
          这是一个使用 Next.js 15 构建的示例项目。
        </p>
        
        <div className="bg-white p-6 rounded-lg shadow-md">
          <h2 className="text-2xl font-semibold mb-4">项目特点</h2>
          <ul className="space-y-2 text-gray-700">
            <li> 使用 App Router(推荐)</li>
            <li> TypeScript 类型安全</li>
            <li> TailwindCSS 原子化样式</li>
            <li> Server Components 性能优化</li>
            <li> Server Actions 简化后端</li>
          </ul>
        </div>
      </div>
    </main>
  );
}

访问 http://localhost:3000/about,查看关于页面。


2.5 编写第一个 API:app/api/route.ts

创建 API 路由

创建 app/api/hello/route.ts

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  return NextResponse.json({
    message: 'Hello from Next.js API!',
    timestamp: new Date().toISOString(),
  });
}

export async function POST(request: Request) {
  const body = await request.json();
  
  return NextResponse.json({
    message: 'POST request received',
    data: body,
  });
}

测试 API

使用浏览器访问 http://localhost:3000/api/hello,查看 GET 响应:

{
  "message": "Hello from Next.js API!",
  "timestamp": "2025-11-16T01:02:31.000Z"
}

使用 curl 测试 POST:

curl -X POST http://localhost:3000/api/hello \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice"}'

响应:

{
  "message": "POST request received",
  "data": {
    "name": "Alice"
  }
}

在页面中调用 API

修改 app/page.tsx,添加 API 调用:

// app/page.tsx
"use client";

import { useState } from 'react';

export default function Home() {
  const [apiResponse, setApiResponse] = useState<string>('');

  const fetchHello = async () => {
    const res = await fetch('/api/hello');
    const data = await res.json();
    setApiResponse(JSON.stringify(data, null, 2));
  };

  return (
    <main className="min-h-screen p-8 bg-gradient-to-br from-blue-50 to-indigo-100">
      <div className="max-w-4xl mx-auto">
        <h1 className="text-5xl font-bold text-gray-900 mb-4">
          Welcome to Next.js 15
        </h1>
        
        <button
          onClick={fetchHello}
          className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition mb-4"
        >
          调用 API
        </button>
        
        {apiResponse && (
          <pre className="bg-gray-900 text-green-400 p-4 rounded-lg overflow-x-auto">
            {apiResponse}
          </pre>
        )}
      </div>
    </main>
  );
}

点击按钮,查看 API 响应。


2.6 引入 TailwindCSS 并配置设计系统基础

TailwindCSS 已内置

如果你在安装时选择了 Tailwind CSS,它已经配置好了。查看相关文件:

tailwind.config.ts

import type { Config } from "tailwindcss";

export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [],
} satisfies Config;

app/globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --background: #ffffff;
  --foreground: #171717;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

body {
  color: var(--foreground);
  background: var(--background);
  font-family: Arial, Helvetica, sans-serif;
}

扩展设计系统

修改 tailwind.config.ts,添加自定义颜色、字体、间距:

import type { Config } from "tailwindcss";

export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
        primary: {
          50: '#f0f9ff',
          100: '#e0f2fe',
          200: '#bae6fd',
          300: '#7dd3fc',
          400: '#38bdf8',
          500: '#0ea5e9',
          600: '#0284c7',
          700: '#0369a1',
          800: '#075985',
          900: '#0c4a6e',
        },
      },
      fontFamily: {
        sans: ['Inter', 'sans-serif'],
        mono: ['Fira Code', 'monospace'],
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
      },
      borderRadius: {
        'xl': '1rem',
        '2xl': '1.5rem',
      },
    },
  },
  plugins: [],
} satisfies Config;

使用自定义样式:

<h1 className="text-4xl font-bold text-primary-600">
  自定义主题色
</h1>

<p className="font-mono text-sm bg-gray-100 p-2 rounded-xl">
  等宽字体 + 圆角
</p>

创建通用组件

创建 components/Button.tsx

// components/Button.tsx
import { ButtonHTMLAttributes } from 'react';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'outline';
  size?: 'sm' | 'md' | 'lg';
}

export default function Button({
  children,
  variant = 'primary',
  size = 'md',
  className = '',
  ...props
}: ButtonProps) {
  const baseStyles = 'rounded-lg font-semibold transition-colors';
  
  const variantStyles = {
    primary: 'bg-primary-600 text-white hover:bg-primary-700',
    secondary: 'bg-gray-600 text-white hover:bg-gray-700',
    outline: 'border-2 border-primary-600 text-primary-600 hover:bg-primary-50',
  };
  
  const sizeStyles = {
    sm: 'px-3 py-1.5 text-sm',
    md: 'px-4 py-2 text-base',
    lg: 'px-6 py-3 text-lg',
  };
  
  return (
    <button
      className={`${baseStyles} ${variantStyles[variant]} ${sizeStyles[size]} ${className}`}
      {...props}
    >
      {children}
    </button>
  );
}

使用组件:

import Button from '@/components/Button';

<Button variant="primary" size="lg">
  主要按钮
</Button>

<Button variant="outline" size="sm">
  边框按钮
</Button>

2.7 一键发布到 Vercel:从本地到线上的完整流程

初始化 Git 仓库

git init
git add .
git commit -m "Initial commit: Next.js 15 project"

推送到 GitHub

  1. 在 GitHub 创建新仓库:https://github.com/new
  2. 推送代码:
git remote add origin https://github.com/your-username/my-nextjs-app.git
git branch -M main
git push -u origin main

部署到 Vercel

方式 1:通过 Vercel Dashboard

  1. 访问 https://vercel.com,登录账号
  2. 点击 “Add New Project”
  3. 导入 GitHub 仓库 my-nextjs-app
  4. 配置:
    • Framework Preset: Next.js
    • Build Command: pnpm build(自动检测)
    • Output Directory: .next(自动检测)
  5. 点击 “Deploy”
  6. 等待 1-2 分钟,部署完成

方式 2:通过 Vercel CLI

npm i -g vercel
vercel

交互式配置:

? Set up and deploy "~\my-nextjs-app"? [Y/n] y
? Which scope do you want to deploy to? your-account
? Link to existing project? [y/N] n
? What's your project's name? my-nextjs-app
? In which directory is your code located? ./
? Want to override the settings? [y/N] n

部署完成后,Vercel 会给你一个 URL,如:

https://my-nextjs-app-xyz.vercel.app

自定义域名

  1. 在 Vercel Dashboard,进入项目设置
  2. 点击 “Domains”
  3. 添加域名:myapp.com
  4. 按照提示配置 DNS:
    • 添加 A 记录:@76.76.21.21
    • 添加 CNAME 记录:wwwcname.vercel-dns.com

环境变量配置

在 Vercel Dashboard → Settings → Environment Variables,添加:

DATABASE_URL=postgresql://...
NEXT_PUBLIC_API_URL=https://api.example.com

或在本地创建 .env.local

DATABASE_URL=postgresql://...
NEXT_PUBLIC_API_URL=https://api.example.com

注意:以 NEXT_PUBLIC_ 开头的环境变量会暴露到客户端,其他变量只在服务端可用。


本章小结

Key Takeaways

  1. 使用 create-next-app 快速初始化项目,选择 TypeScript + ESLint + TailwindCSS + App Router
  2. 目录结构清晰app/ 存放页面和布局,public/ 存放静态资源,components/ 存放组件
  3. 开发体验优秀:HMR 热更新、TypeScript 类型提示、Tailwind 自动补全
  4. 第一个页面app/page.tsx 是根页面,app/about/page.tsx/about 页面
  5. 第一个 APIapp/api/hello/route.ts 定义 GET / POST 等 HTTP 方法
  6. TailwindCSS 可定制:扩展 tailwind.config.ts 添加自定义颜色、字体、间距
  7. Vercel 一键部署:Git 推送到 GitHub,Vercel 自动部署,支持自定义域名

下一步

在下一章,我们将深入 App Router 的目录结构,理解 Layouts、Pages、Templates 的区别,以及嵌套路由、并行路由、拦截路由等高级特性。


参考资料

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页