Skip to content

Next.js

Next.js 生产级 React 框架的全面指南。

Next.js 简介

Next.js 是一个 React 框架,它支持服务器端渲染、静态站点生成、API 路由等功能。它通过快速刷新和零配置等特性提供了出色的开发体验。

Next.js 的主要特点

  • 混合渲染:服务器端渲染 (SSR)、静态站点生成 (SSG) 和增量静态再生成 (ISR)
  • 基于文件的路由:基于文件结构的自动路由
  • API 路由:作为 Next.js 应用程序的一部分创建 API 端点
  • 内置 CSS 支持:支持 CSS 模块、Sass 和其他样式解决方案
  • 图像优化:使用 Image 组件自动优化图像
  • 零配置:开箱即用,具有合理的默认设置
  • TypeScript 支持:内置 TypeScript 支持
  • 快速刷新:开发过程中即时反馈

Next.js 入门

前提条件

在开始使用 Next.js 之前,您应该具备:

  • Node.js 14.6.0 或更新版本
  • JavaScript 和 React 的基础知识
  • 代码编辑器(推荐 VS Code)

创建 Next.js 项目

bash
# 创建新的 Next.js 项目
npx create-next-app@latest my-next-app

# 导航到项目目录
cd my-next-app

# 启动开发服务器
npm run dev

项目结构

典型的 Next.js 项目结构如下:

my-next-app/
├── .next/            # 构建输出(自动生成)
├── node_modules/     # Node.js 依赖
├── pages/            # 页面组件和 API 路由
│   ├── api/          # API 路由
│   ├── _app.js       # 自定义 App 组件
│   ├── _document.js  # 自定义 Document 组件
│   └── index.js      # 首页
├── public/           # 静态资源
├── styles/           # CSS 文件
├── components/       # React 组件
├── lib/              # 实用函数
├── .eslintrc.json    # ESLint 配置
├── next.config.js    # Next.js 配置
├── package.json      # 项目依赖和脚本
└── README.md         # 项目文档

核心概念

1. 页面和路由

Next.js 有一个基于文件系统的路由器,建立在页面概念之上。当文件添加到 pages 目录时,它会自动作为路由可用。

jsx
// pages/index.js - 访问路径为 /
export default function Home() {
  return <h1>你好,Next.js!</h1>;
}

// pages/about.js - 访问路径为 /about
export default function About() {
  return <h1>关于我们</h1>;
}

// pages/posts/[id].js - 访问路径为 /posts/1, /posts/2 等
import { useRouter } from 'next/router';

export default function Post() {
  const router = useRouter();
  const { id } = router.query;

  return <h1>文章: {id}</h1>;
}

2. 数据获取

Next.js 提供了几种为页面获取数据的方法:

getStaticProps(静态生成)

jsx
// 此函数在生产环境中构建时运行
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: { data }, // 将作为 props 传递给页面组件
    revalidate: 60, // 可选:60 秒后重新生成页面
  };
}

export default function Page({ data }) {
  return <div>{data.title}</div>;
}

getStaticPaths(带动态路由的静态生成)

jsx
// 此函数在生产环境中构建时运行
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 或 false
  };
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  return {
    props: { post },
  };
}

export default function Post({ post }) {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

getServerSideProps(服务器端渲染)

jsx
// 此函数在每次请求时运行
export async function getServerSideProps(context) {
  const res = await fetch(`https://api.example.com/data?q=${context.query.q}`);
  const data = await res.json();

  return {
    props: { data }, // 将作为 props 传递给页面组件
  };
}

export default function Page({ data }) {
  return <div>{data.title}</div>;
}

客户端数据获取

jsx
import { useState, useEffect } from 'react';

export default function Page() {
  const [data, setData] = useState(null);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, []);

  if (isLoading) return <p>加载中...</p>;
  if (!data) return <p>无数据</p>;

  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.description}</p>
    </div>
  );
}

3. API 路由

Next.js 允许您通过向 pages/api 目录添加文件来创建 API 端点作为应用程序的一部分。

jsx
// pages/api/hello.js
export default function handler(req, res) {
  res.status(200).json({ name: '张三' });
}

// pages/api/users/[id].js
export default function userHandler(req, res) {
  const { id } = req.query;
  const { method } = req;

  switch (method) {
    case 'GET':
      // 从数据库获取用户数据
      res.status(200).json({ id, name: `用户 ${id}` });
      break;
    case 'PUT':
      // 在数据库中更新用户数据
      res.status(200).json({ id, name: req.body.name });
      break;
    default:
      res.setHeader('Allow', ['GET', 'PUT']);
      res.status(405).end(`方法 ${method} 不允许`);
  }
}

4. 样式

Next.js 支持各种样式方法:

CSS 模块

jsx
// styles/Home.module.css
.container {
  padding: 0 2rem;
}

.title {
  margin: 0;
  line-height: 1.15;
  font-size: 4rem;
  color: #0070f3;
}

// pages/index.js
import styles from '../styles/Home.module.css';

export default function Home() {
  return (
    <div className={styles.container}>
      <h1 className={styles.title}>欢迎使用 Next.js!</h1>
    </div>
  );
}

全局 CSS

jsx
// styles/globals.css
html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

// pages/_app.js
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

CSS-in-JS(Styled JSX)

jsx
export default function Button() {
  return (
    <div>
      <button>点击我</button>
      <style jsx>{`
        button {
          background: #0070f3;
          color: white;
          border: none;
          padding: 0.5rem 1rem;
          border-radius: 4px;
          cursor: pointer;
        }
        button:hover {
          background: #0051a2;
        }
      `}</style>
    </div>
  );
}

5. 图像优化

Next.js 提供了一个 Image 组件,可以优化图像以提高性能。

jsx
import Image from 'next/image';

export default function Home() {
  return (
    <div>
      <h1>我的主页</h1>
      <Image
        src="/profile.jpg"
        alt="个人照片"
        width={500}
        height={300}
        priority
      />
      <p>欢迎访问我的网站!</p>
    </div>
  );
}

高级功能

1. 中间件

中间件允许您在请求完成之前运行代码。

jsx
// middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  // 检查用户是否已认证
  const isAuthenticated = checkAuth(request);
  
  if (!isAuthenticated) {
    // 如果未认证,重定向到登录页面
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  return NextResponse.next();
}

export const config = {
  matcher: '/dashboard/:path*',
};

2. 国际化(i18n)

Next.js 支持国际化路由和内容。

jsx
// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'zh', 'ja'],
    defaultLocale: 'zh',
  },
};

// pages/index.js
import { useRouter } from 'next/router';

export default function Home() {
  const router = useRouter();
  const { locale } = router;

  return (
    <div>
      <h1>{locale === 'en' ? 'Welcome' : locale === 'zh' ? '欢迎' : 'ようこそ'}</h1>
      <button onClick={() => router.push('/', '/', { locale: 'en' })}>English</button>
      <button onClick={() => router.push('/', '/', { locale: 'zh' })}>中文</button>
      <button onClick={() => router.push('/', '/', { locale: 'ja' })}>日本語</button>
    </div>
  );
}

3. 认证

Next.js 可以与认证提供商(如 NextAuth.js)集成。

jsx
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

export default NextAuth({
  providers: [
    Providers.GitHub({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    Providers.Google({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
  ],
  database: process.env.DATABASE_URL,
  session: {
    jwt: true,
  },
  callbacks: {
    async session(session, user) {
      session.user.id = user.id;
      return session;
    },
  },
});

// pages/profile.js
import { useSession, signIn, signOut } from 'next-auth/react';

export default function Profile() {
  const { data: session } = useSession();

  if (!session) {
    return (
      <div>
        <p>您尚未登录</p>
        <button onClick={() => signIn()}>登录</button>
      </div>
    );
  }

  return (
    <div>
      <p>欢迎,{session.user.name}!</p>
      <button onClick={() => signOut()}>登出</button>
    </div>
  );
}

部署

Vercel(推荐)

bash
# 安装 Vercel CLI
npm install -g vercel

# 部署
vercel

其他平台

Next.js 可以部署到任何支持 Node.js 的平台,例如:

  • AWS Amplify
  • Netlify
  • DigitalOcean App Platform
  • Google Cloud Run
  • Heroku

最佳实践

1. 代码组织

  • 保持页面简单,专注于渲染
  • 将业务逻辑提取到单独的文件中
  • 使用一致的文件夹结构
  • 将相关组件和实用工具分组

2. 性能优化

  • 为每个用例使用适当的数据获取方法
  • 使用动态导入实现代码分割
  • 使用 Next.js Image 组件优化图像
  • 最小化客户端 JavaScript
  • 使用内置性能分析
jsx
// 动态导入示例
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/heavy-component'), {
  loading: () => <p>加载中...</p>,
  ssr: false, // 禁用服务器端渲染
});

export default function Page() {
  return (
    <div>
      <h1>我的页面</h1>
      <DynamicComponent />
    </div>
  );
}

3. SEO 最佳实践

jsx
import Head from 'next/head';

export default function Page({ product }) {
  return (
    <div>
      <Head>
        <title>{product.name} | 我的商店</title>
        <meta name="description" content={product.description} />
        <meta property="og:title" content={product.name} />
        <meta property="og:description" content={product.description} />
        <meta property="og:image" content={product.image} />
        <link rel="canonical" href={`https://mystore.com/products/${product.id}`} />
      </Head>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
    </div>
  );
}

常见问题解答

Next.js 与 Create React App 有何不同?

Next.js 提供服务器端渲染、静态站点生成和 API 路由,而 Create React App 仅专注于客户端渲染。

什么时候应该使用 getStaticProps 与 getServerSideProps?

  • 当数据可以在构建时获取且不经常变化时,使用 getStaticProps
  • 当数据需要在每个请求上获取或依赖于请求参数时,使用 getServerSideProps

如何在 Next.js 中处理认证?

您可以使用 NextAuth.js、Auth0 或 Firebase Authentication 等库,或使用 API 路由和 cookie 实现自己的认证系统。

我可以在 Next.js 中使用 Redux 吗?

是的,您可以在 Next.js 中使用 Redux。您需要设置 Redux store 并用 provider 包装您的应用程序。

如何部署 Next.js 应用程序?

最简单的方法是部署到 Vercel,这是 Next.js 创建者构建的平台。您也可以部署到 Netlify、AWS 或任何支持 Node.js 的平台。

相关资源


本文档将持续更新,如有问题请通过 GitHub Issues 反馈。

vitepress开发指南