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开发指南