Next.js 教程

Next.js 教程

Next.js 是 Vercel 开发的 React 全栈框架,提供服务端渲染(SSR)、静态生成(SSG)、文件路由、API 路由等能力,是构建现代 Web 应用的主流选择。

核心特性

  • 混合渲染:SSR、SSG、CSR、ISR 按需组合
  • 文件路由:pages/app 目录即路由,无需配置
  • 全栈能力:API Routes 写后端接口
  • 内置优化:图片、字体、SEO、代码分割
  • 生态完善:与 React、Tailwind、Vercel 深度集成

为什么选择 Next.js

SEO 友好        服务端渲染利于搜索引擎
性能优秀        自动优化、流式渲染
开发体验        热更新、TypeScript 支持
部署简单        Vercel 一键部署
全栈能力        前后端一个项目

版本说明

Next.js 13     App Router(实验)
Next.js 14     App Router 稳定
Next.js 15     当前稳定版(2024)

# Pages Router(旧) vs App Router(新)
# 新项目推荐 App Router

学习路线

  1. 安装与项目结构
  2. 页面与路由
  3. 数据获取
  4. CSS 样式与 Tailwind
  5. 图片、字体、组件与布局

Next.js 简介

Next.js 简介

1. 发展历史

2016 年  由 Zeit(Vercel)发布
2019 年  Next.js 9 引入 API Routes
2020 年  Next.js 10 内置图片优化
2022 年  Next.js 13 App Router
2024 年  Next.js 15

# 被广泛使用
# Vercel、Netflix、TikTok、Twitch 等使用

2. 核心概念

  • 渲染模式:SSR(服务端)/ SSG(静态)/ CSR(客户端)/ ISR(增量)
  • 路由:文件系统路由(页面即 URL)
  • 数据获取:getServerSideProps、getStaticProps、Server Components
  • 布局:共享布局、嵌套布局
  • 中间件:请求拦截与重定向

3. 渲染模式对比

# SSR(服务端渲染)
- 每次请求生成 HTML
- 适合实时数据

# SSG(静态生成)
- 构建时生成 HTML
- 适合内容型页面,极快

# ISR(增量静态再生成)
- 静态 + 定时重新生成
- 内容更新不频繁的页面

# CSR(客户端渲染)
- 浏览器渲染
- 适合交互型应用

4. 适用场景

  • 营销页、博客、文档(SEO 重要)
  • 电商、内容平台
  • 全栈应用(带后端接口)
  • Dashboard 应用

Next.js 安装

Next.js 安装

1. 环境要求

Node.js 18.18+ 或 20.9+
npm / yarn / pnpm

2. 创建项目

# 自动创建(create-next-app)
npx create-next-app@latest my-app

# 交互式配置
# - TypeScript? 是
# - ESLint? 是
# - Tailwind CSS? 是/否
# - App Router? 是
# - src/ 目录? 是
# - import 别名? 是

# 进入项目
cd my-app

# 启动开发
npm run dev
# 访问 http://localhost:3000

3. 常用命令

npm run dev        开发模式(热更新)
npm run build      生产构建
npm run start      启动生产服务
npm run lint       代码检查

# 生产部署
npm run build
npm run start

4. 第一个页面

# app/page.tsx
export default function Home() {
  return (
    <main>
      <h1>Hello Next.js!</h1>
      <p>我的第一个 Next.js 应用</p>
    </main>
  );
}

5. 项目模板

# 官方示例
npx create-next-app --example blog-starter
npx create-next-app --example with-tailwindcss
npx create-next-app --example with-typescript

Next.js 目录结构

Next.js 目录结构

1. 标准目录

my-app/
├── app/                  # App Router 页面
│   ├── layout.tsx        # 根布局
│   ├── page.tsx          # 首页
│   ├── about/
│   │   └── page.tsx      # /about 页面
│   └── api/
│       └── hello/
│           └── route.ts  # API 接口
├── components/           # 组件
├── lib/                  # 工具函数
├── public/               # 静态资源
│   └── images/
├── styles/               # 样式
├── next.config.js        # Next 配置
├── package.json
└── tsconfig.json         # TypeScript

2. 文件约定

page.tsx        页面组件(路由入口)
layout.tsx      布局组件(共享)
loading.tsx     加载状态
error.tsx       错误处理
not-found.tsx   404 页面
route.ts        API 路由(App Router)
middleware.ts   中间件
sitemap.ts      站点地图
robots.ts       robots.txt

3. App Router 结构

# 文件夹 = 路由段
app/
├── page.tsx           → /
├── about/page.tsx     → /about
├── blog/
│   ├── page.tsx       → /blog
│   └── [slug]/
│       └── page.tsx   → /blog/动态slug

# 动态路由 [参数]
# 并行路由 @slot
# 拦截路由 (.)xxx

4. 根布局

// app/layout.tsx
import "./globals.css";

export const metadata = {
  title: "我的站点",
  description: "Next.js 示例",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body>{children}</body>
    </html>
  );
}

Next.js 项目说明

Next.js 项目说明

1. 配置文件

// next.config.js
/** @type {import("next").NextConfig} */
const nextConfig = {
  // 图片域名白名单
  images: {
    remotePatterns: [
      { hostname: "example.com" },
    ],
  },
  // 重定向
  async redirects() {
    return [
      { source: "/old", destination: "/new", permanent: true },
    ];
  },
  // 环境变量
  env: {
    API_URL: process.env.API_URL,
  },
};

module.exports = nextConfig;

2. 环境变量

# .env.local(本地,不入 git)
DATABASE_URL=mysql://...
API_KEY=xxx

# .env.production(生产)
# .env.development(开发)

# 使用
# 服务端组件直接读取
process.env.DATABASE_URL

# 客户端组件需 NEXT_PUBLIC_ 前缀
NEXT_PUBLIC_API_URL=https://api.example.com

3. TypeScript

# Next.js 内置 TypeScript 支持
# 自动生成 tsconfig.json

// 类型定义
interface Post {
  id: number;
  title: string;
  content: string;
}

// 组件 props
interface Props {
  post: Post;
}

export default function PostCard({ post }: Props) {
  return <h2>{post.title}</h2>;
}

4. 代码规范

# ESLint + Prettier 内置
npm run lint

# 目录组织建议
- components/ui     基础组件
- components/features 业务组件
- lib/api           API 封装
- hooks             自定义 hooks

# 命名约定
- 组件: PascalCase(PostCard)
- 文件: kebab-case(post-card.tsx)
- 函数: camelCase(getPost)

Next.js 页面和路由

Next.js 页面和路由

1. 静态路由

# app/about/page.tsx → /about
export default function AboutPage() {
  return <h1>关于我们</h1>;
}

# app/contact/page.tsx → /contact
# app/blog/page.tsx → /blog

2. 动态路由

# app/blog/[slug]/page.tsx → /blog/hello-world
interface Props {
  params: { slug: string };
}

export default function BlogPost({ params }: Props) {
  return <h1>文章: {params.slug}</h1>;
}

# 多参数
# app/[category]/[id]/page.tsx
# /tech/123 → params: {category: "tech", id: "123"}

# 捕获全部
# app/[...slug]/page.tsx
# /a/b/c → params: {slug: ["a", "b", "c"]}

3. 链接导航

// 使用 Link 组件(客户端导航)
import Link from "next/link";

export default function Nav() {
  return (
    <nav>
      <Link href="/">首页</Link>
      <Link href="/about">关于</Link>
      <Link href="/blog/my-post">文章</Link>
    </nav>
  );
}

// 编程式导航
import { useRouter } from "next/navigation";

const router = useRouter();
router.push("/about");
router.replace("/about");
router.back();

4. 导航与路由组

# 路由组(不影响 URL)
# app/(marketing)/about/page.tsx → /about
# app/(dashboard)/settings/page.tsx → /settings

# 好处
- 不同区域用不同布局
- 组织代码结构

5. 404 页面

// app/not-found.tsx
export default function NotFound() {
  return (
    <div>
      <h1>404 - 页面不存在</h1>
      <Link href="/">返回首页</Link>
    </div>
  );
}

Next.js 数据获取

Next.js 数据获取

1. Server Components(推荐)

// app/blog/page.tsx(服务端组件,默认)
// 直接在组件中异步获取数据

async function getPosts() {
  const res = await fetch(
    "https://api.example.com/posts",
    { cache: "no-store" }   // 每次请求最新
  );
  return res.json();
}

export default async function BlogPage() {
  const posts = await getPosts();

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

2. 缓存策略

// 静态生成(默认,构建时获取)
fetch(url)  或  cache: "force-cache"

// 每次请求重新获取
fetch(url, { cache: "no-store" })

// 增量再生成(ISR)
fetch(url, { next: { revalidate: 60 } })
// 每 60 秒重新生成一次

3. getStaticProps(Pages Router)

// pages/index.tsx(旧版)
export async function getStaticProps() {
  const posts = await getPosts();
  return { props: { posts } };
}

// 带 ISR
export async function getStaticProps() {
  return {
    props: { posts },
    revalidate: 60,      // 60 秒更新
  };
}

4. 客户端获取

"use client";   // 客户端组件

import { useEffect, useState } from "react";

export default function ClientData() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("/api/data")
      .then((res) => res.json())
      .then(setData);
  }, []);

  return <div>{JSON.stringify(data)}</div>;
}

5. 数据获取最佳实践

# 优先 Server Components(性能、SEO)
# 页面静态部分用 SSG,动态部分用 SSR/ISR
# 客户端只做交互数据
# 用 SWR/React Query 管理客户端状态

Next.js CSS 样式

Next.js CSS 样式

1. 全局样式

// app/globals.css
body {
  font-family: system-ui, sans-serif;
  margin: 0;
  background: #f8f9fa;
}

// app/layout.tsx 引入
import "./globals.css";

2. CSS Modules

// components/Button.module.css
.btn {
  padding: 8px 16px;
  background: #3498db;
  color: #fff;
  border: none;
  border-radius: 6px;
}
.btn:hover {
  background: #2980b9;
}

// components/Button.tsx
import styles from "./Button.module.css";

export default function Button() {
  return <button className={styles.btn}>按钮</button>;
}

3. 样式库

# Tailwind CSS(内置支持)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

# tailwind.config.ts
content: ["./app/**/*.{ts,tsx}"]

// app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

// 使用
export default function Page() {
  return (
    <div className="flex min-h-screen items-center">
      <h1 className="text-2xl font-bold">标题</h1>
    </div>
  );
}

4. CSS-in-JS

# styled-components
npm install styled-components

"use client";
import styled from "styled-components";

const Title = styled.h1`
  color: #2c3e50;
  font-size: 24px;
`;

export default function Page() {
  return <Title>样式组件</Title>;
}

5. 样式最佳实践

  • 全局样式: globals.css(主题变量)
  • 组件样式: CSS Modules 或 Tailwind
  • 避免内联样式过多
  • 使用 CSS 变量管理主题

Next.js Tailwind CSS

Next.js Tailwind CSS

1. 安装配置

# create-next-app 时选择 Tailwind
# 或手动安装
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# tailwind.config.ts
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./app/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
  ],
  theme: {
    extend: {
      colors: {
        primary: "#3498db",
      },
    },
  },
};

export default config;

2. 常用类

// 布局
flex / grid / block / hidden
flex-col / flex-row / items-center / justify-between
gap-4 / space-y-4 / p-4 / m-4

// 尺寸
w-full / w-1/2 / max-w-2xl / h-screen

// 文字
text-sm / text-xl / font-bold / text-center
text-gray-500 / text-primary

// 背景与边框
bg-white / bg-primary / rounded-lg
border / border-gray-200 / shadow-md

// 响应式
sm: / md: / lg: / xl: 前缀
md:flex / lg:text-2xl

// 状态
hover:bg-blue-600 / focus:ring-2

3. 页面示例

export default function Home() {
  return (
    <main className="min-h-screen bg-gray-50">
      <div className="max-w-4xl mx-auto py-12 px-4">
        <h1 className="text-3xl font-bold text-gray-900">
          欢迎使用 Tailwind
        </h1>
        <p className="mt-4 text-gray-600">
          使用 Tailwind 快速构建界面
        </p>
        <button className="mt-6 px-4 py-2 bg-blue-500
          text-white rounded-lg hover:bg-blue-600">
          开始
        </button>
      </div>
    </main>
  );
}

4. 深色模式

// tailwind.config.ts
darkMode: "class",

// 使用
<div className="bg-white dark:bg-gray-900">
  <p className="text-gray-900 dark:text-white">内容</p>
</div>

Next.js 图片和字体

Next.js 图片和字体

1. 图片组件

import Image from "next/image";

export default function Hero() {
  return (
    <Image
      src="/images/hero.png"
      alt="宣传图"
      width={800}
      height={400}
      priority          // 首屏优先加载
      className="rounded-lg"
    />
  );
}

// 远程图片需配置域名
// next.config.js
images: {
  remotePatterns: [
    { hostname: "images.example.com" },
  ],
}

2. 图片优化特性

# next/image 自动优化
- WebP/AVIF 格式转换
- 响应式尺寸(srcset)
- 懒加载(默认)
- 占位符(blur placeholder)
- 防 CLS(布局偏移)

// 填充容器
<Image
  src="/images/banner.jpg"
  alt="横幅"
  fill
  sizes="100vw"
  style={{ objectFit: "cover" }}
/>

3. 字体优化

import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"] });

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="zh-CN">
      <body className={inter.className}>
        {children}
      </body>
    </html>
  );
}

// 本地字体
import localFont from "next/font/local";

const myFont = localFont({
  src: "./fonts/MyFont.woff2",
});

4. 元数据 SEO

// app/layout.tsx
export const metadata = {
  title: {
    default: "我的站点",
    template: "%s | 我的站点",
  },
  description: "站点描述",
  keywords: ["Next.js", "React"],
  openGraph: {
    title: "我的站点",
    description: "描述",
    images: ["/og-image.png"],
  },
};

Next.js 组件与布局

Next.js 组件与布局

1. 服务端 vs 客户端组件

# 服务端组件(默认)
# - 在服务器渲染
# - 不能使用 useState/useEffect
# - 直接访问数据库/文件

// app/server-page.tsx
export default async function ServerPage() {
  const data = await fetchData();
  return <div>{data}</div>;
}

# 客户端组件
# - "use client" 标记
# - 支持 Hooks、事件处理

// app/client-page.tsx
"use client";
import { useState } from "react";

export default function ClientPage() {
  const [count, setCount] = useState(0);
  return (
    <button => setCount(c => c + 1)}>
      点击 {count}
    </button>
  );
}

2. 嵌套布局

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex">
      <aside>侧边栏</aside>
      <main>{children}</main>
    </div>
  );
}

// 嵌套后
// /dashboard 页面自动包含侧边栏布局

3. 组合模式

// 服务端组件包裹客户端组件
// app/page.tsx
import ClientComponent from "./ClientComponent";

export default function Page() {
  return (
    <div>
      <h1>服务端渲染的标题</h1>
      <ClientComponent />
    </div>
  );
}

// 避免把客户端组件做布局容器
// 交互组件尽量"叶子化"

4. 组件组织

components/
├── ui/           基础组件(Button, Card)
├── layout/       布局组件(Nav, Footer)
├── features/     业务组件(UserList)
└── shared/       共享工具

// 组件 props 建议
// 用 TypeScript interface 定义
interface CardProps {
  title: string;
  children: React.ReactNode;
  className?: string;
}

5. 性能优化

# 懒加载组件
import dynamic from "next/dynamic";

const HeavyComponent = dynamic(
  () => import("./HeavyComponent"),
  { loading: () => <p>加载中...</p> }
);

# React.memo(客户端组件)
const Memoized = React.memo(MyComponent);

# Suspense 流式渲染
import { Suspense } from "react";
<Suspense fallback={<p>加载中</p>}>
  <SlowComponent />
</Suspense>
第 1 / 11 节