Next.js 中文教程
1. 什么是 Next.js?
Next.js 是一个基于 React 的开源前端框架,由 Vercel 公司开发和维护。它提供了服务器端渲染 (SSR)、静态站点生成 (SSG)、路由、API 路由等功能,使开发者能够构建现代化、高性能的 Web 应用。
1.1 Next.js 的核心优势
- **服务器端渲染 (SSR)**:提高首屏加载速度和 SEO 友好性。
- **静态站点生成 (SSG)**:预渲染页面,提供极致的加载性能。
- 自动代码拆分:减小初始加载体积,提高页面加载速度。
- 内置路由:基于文件系统的路由,无需额外配置。
- API 路由:在同一代码库中构建 API 接口。
- 开发体验:热模块替换、快速刷新等功能。
- 部署便捷:与 Vercel 平台无缝集成,也支持其他部署方式。
2. 安装和初始化
2.1 使用 create-next-app 初始化项目
# 使用 npm
npx create-next-app@latest
# 使用 yarn
yarn create next-app
# 使用 pnpm
pnpm create next-app2.2 选择模板
create-next-app 会提示你选择项目配置:
- TypeScript:是否使用 TypeScript
- ESLint:是否集成 ESLint
- Tailwind CSS:是否集成 Tailwind CSS
- src/ 目录:是否使用 src/ 目录结构
- App Router:是否使用 App Router(Next.js 13+ 新特性)
2.3 手动安装
如果你想手动安装 Next.js,可以按照以下步骤:
# 创建项目目录
mkdir my-next-app
cd my-next-app
# 初始化 package.json
npm init -y
# 安装依赖
npm install next react react-dom
# 添加脚本
# 在 package.json 中添加:
# "scripts": {
# "dev": "next dev",
# "build": "next build",
# "start": "next start",
# "lint": "next lint"
# }2.4 验证安装
# 启动开发服务器
npm run dev
# 访问 http://localhost:30003. 基本目录结构
Next.js 项目的默认目录结构如下:
my-next-app/
├── app/ # App Router 目录(Next.js 13+)
│ ├── page.js # 首页
│ ├── layout.js # 布局组件
│ └── api/ # API 路由
├── pages/ # 页面路由目录
│ ├── index.js # 首页
│ ├── about.js # 关于页面
│ └── api/ # API 路由
├── public/ # 静态资源目录
├── styles/ # 样式文件目录
├── components/ # 组件目录
├── lib/ # 工具函数目录
├── next.config.js # Next.js 配置文件
├── package.json # 项目配置文件
└── README.md # 项目说明文件4. 核心功能
4.1 路由系统
Next.js 提供了两种路由系统:
4.1.1 Pages Router(传统路由)
基于文件系统的路由,文件路径即为路由路径:
pages/index.js→/pages/about.js→/aboutpages/blog/[id].js→/blog/123(动态路由)pages/blog/[...slug].js→/blog/2023/01/01(捕获所有路由)
动态路由示例:
// pages/blog/[id].js
import { useRouter } from 'next/router';
export default function BlogPost() {
const router = useRouter();
const { id } = router.query;
return <div>Blog Post: {id}</div>;
}4.1.2 App Router(Next.js 13+)
基于 React Server Components 的新路由系统:
app/page.js→/app/about/page.js→/aboutapp/blog/[id]/page.js→/blog/123(动态路由)app/blog/[...slug]/page.js→/blog/2023/01/01(捕获所有路由)
动态路由示例:
// app/blog/[id]/page.js
export default function BlogPost({ params }) {
const { id } = params;
return <div>Blog Post: {id}</div>;
}4.2 数据获取
Next.js 提供了多种数据获取方式,根据渲染策略选择合适的方法:
4.2.1 静态生成(SSG)
使用 getStaticProps 获取数据并预渲染页面:
// pages/blog.js
export async function getStaticProps() {
// 获取博客文章数据
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: {
posts,
},
// 可选:设置重验证时间(秒)
revalidate: 60,
};
}
export default function Blog({ posts }) {
return (
<div>
<h1>Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}4.2.2 服务器端渲染(SSR)
使用 getServerSideProps 在每次请求时获取数据:
// pages/user.js
export async function getServerSideProps(context) {
// 从请求中获取用户 ID
const { id } = context.query;
// 获取用户数据
const res = await fetch(`https://api.example.com/users/${id}`);
const user = await res.json();
return {
props: {
user,
},
};
}
export default function User({ user }) {
return <div>User: {user.name}</div>;
}4.2.3 客户端数据获取
对于不需要 SEO 的数据,可以在客户端获取:
// pages/dashboard.js
import { useState, useEffect } from 'react';
export default function Dashboard() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
const res = await fetch('/api/dashboard');
const result = await res.json();
setData(result);
setLoading(false);
}
fetchData();
}, []);
if (loading) return <div>Loading...</div>;
return (
<div>
<h1>Dashboard</h1>
{/* 显示数据 */}
</div>
);
}4.3 API 路由
Next.js 允许你在 pages/api 或 app/api 目录中创建 API 接口:
4.3.1 Pages Router API 路由
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' });
}4.3.2 App Router API 路由
// app/api/hello/route.js
export async function GET(request) {
return new Response(JSON.stringify({ name: 'John Doe' }), {
headers: {
'content-type': 'application/json',
},
});
}4.4 样式解决方案
Next.js 支持多种样式解决方案:
4.4.1 CSS 模块
// components/Button.module.css
.button {
padding: 10px 20px;
background-color: #0070f3;
color: white;
border: none;
border-radius: 4px;
}
// components/Button.js
import styles from './Button.module.css';
export default function Button({ children }) {
return <button className={styles.button}>{children}</button>;
}4.4.2 Tailwind CSS
Next.js 13+ 提供了内置的 Tailwind CSS 支持:
// app/page.js
export default function Home() {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-4xl font-bold">Hello Next.js</h1>
<p className="text-gray-600 mt-4">Welcome to Next.js with Tailwind CSS</p>
</div>
);
}4.4.3 全局样式
// styles/globals.css
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
// pages/_app.js
import '../styles/globals.css';
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}5. 高级特性
5.1 中间件
Next.js 中间件允许你在请求到达页面或 API 路由之前运行代码:
// middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
// 检查是否有认证令牌
const token = request.cookies.get('auth-token');
// 如果没有令牌,重定向到登录页
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
// 继续处理请求
return NextResponse.next();
}
// 定义中间件适用的路径
export const config = {
matcher: '/dashboard/:path*',
};5.2 增量静态再生
增量静态再生(ISR)允许你在构建后更新静态页面:
// pages/blog/[id].js
export async function getStaticPaths() {
// 获取初始路径
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
const paths = posts.map((post) => ({
params: { id: post.id.toString() },
}));
return {
paths,
fallback: 'blocking', // 或 true
};
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`);
const post = await res.json();
return {
props: {
post,
},
revalidate: 60, // 每 60 秒重新生成
};
}
export default function BlogPost({ post }) {
return (
<div>
<h1>{post.title}</h1>
<div>{post.content}</div>
</div>
);
}5.3 图像优化
Next.js 提供了内置的图像优化功能:
// components/OptimizedImage.js
import Image from 'next/image';
export default function OptimizedImage() {
return (
<div>
<h2>优化后的图像</h2>
<Image
src="/vercel.svg"
alt="Vercel Logo"
width={120}
height={40}
// 可选:布局模式
// layout="responsive"
// 可选:加载策略
// loading="lazy"
/>
</div>
);
}5.4 国际化
Next.js 支持国际化路由和内容:
// next.config.js
module.exports = {
i18n: {
locales: ['en', 'zh'],
defaultLocale: 'en',
},
};
// pages/index.js
export default function Home({ locale }) {
return (
<div>
<h1>{locale === 'en' ? 'Hello' : '你好'}</h1>
</div>
);
}6. 最佳实践
6.1 性能优化
- 使用静态生成:对于内容不经常变化的页面,使用 SSG 提高性能。
- 增量静态再生:对于频繁变化的内容,使用 ISR 平衡性能和新鲜度。
- 代码拆分:使用动态导入减少初始加载体积。
- 图像优化:使用
next/image组件优化图像加载。 - 缓存策略:合理设置缓存头,减少重复请求。
6.2 目录结构
my-next-app/
├── app/ # App Router 目录(Next.js 13+)
│ ├── layout.js # 根布局
│ ├── page.js # 首页
│ ├── about/ # 关于页面
│ │ └── page.js
│ └── api/ # API 路由
├── components/ # 可复用组件
│ ├── ui/ # UI 组件
│ └── layout/ # 布局组件
├── lib/ # 工具函数和数据获取
├── public/ # 静态资源
├── styles/ # 全局样式
├── next.config.js # Next.js 配置
└── package.json # 项目配置6.3 部署策略
6.3.1 Vercel 部署
- 将代码推送到 GitHub、GitLab 或 Bitbucket
- 在 Vercel 上导入项目
- 配置构建和部署设置
- 部署完成后获得一个 URL
6.3.2 其他部署方式
- Docker:使用 Docker 容器化部署
- Node.js 服务器:使用
next start启动服务器 - 静态托管:对于纯静态网站,使用
next export导出静态文件
6.4 安全最佳实践
- 环境变量:使用
.env.local存储敏感信息 - API 路由保护:验证 API 请求的身份认证
- 输入验证:验证用户输入,防止 XSS 和 SQL 注入
- CORS 配置:合理配置 CORS 策略
- 依赖管理:定期更新依赖,修复安全漏洞
7. 常见问题与解决方案
7.1 路由问题
问题:动态路由参数获取不到
解决方案:
- 使用
useRouter()钩子获取路由参数(Pages Router) - 使用
params属性获取路由参数(App Router) - 确保文件名格式正确,如
[id].js
7.2 数据获取问题
问题:getStaticProps 不更新数据
解决方案:
- 添加
revalidate属性启用增量静态再生 - 检查数据获取逻辑是否正确
- 确保 API 接口返回最新数据
7.3 样式问题
问题:CSS 模块不生效
解决方案:
- 确保文件名以
.module.css结尾 - 正确导入和使用样式
- 检查 CSS 选择器是否正确
7.4 部署问题
问题:Vercel 部署失败
解决方案:
- 检查构建日志,查看具体错误信息
- 确保环境变量配置正确
- 检查依赖是否正确安装
7.5 性能问题
问题:页面加载缓慢
解决方案:
- 使用静态生成或增量静态再生
- 优化图像和资源
- 减少初始加载体积
- 使用代码拆分
8. 总结
Next.js 是一个功能强大的 React 框架,它提供了服务器端渲染、静态生成、路由、API 路由等核心功能,使开发者能够构建现代化、高性能的 Web 应用。
通过本教程,你应该已经了解了 Next.js 的基本概念、核心功能和最佳实践。Next.js 不断发展,新的特性和改进不断推出,建议你关注官方文档和更新日志,保持对最新功能的了解。
Next.js 的优势在于它提供了一套完整的解决方案,从开发到部署的全流程支持,使开发者能够专注于业务逻辑的实现,而不是基础设施的搭建。无论是构建个人网站、企业应用还是电商平台,Next.js 都能满足你的需求。