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
学习路线
- 安装与项目结构
- 页面与路由
- 数据获取
- CSS 样式与 Tailwind
- 图片、字体、组件与布局
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 节