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-app

2.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:3000

3. 基本目录结构

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/about
  • pages/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/about
  • app/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/apiapp/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 部署

  1. 将代码推送到 GitHub、GitLab 或 Bitbucket
  2. 在 Vercel 上导入项目
  3. 配置构建和部署设置
  4. 部署完成后获得一个 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 都能满足你的需求。

« 上一篇 pnpm 中文教程 下一篇 » Nuxt.js 中文教程