Next.js 是一个基于 React全栈 (Full-stack) Web 框架,由 Vercel 公司开发和维护。它提供了一个开箱即用的解决方案,用于构建高性能、SEO 友好且易于扩展的现代 React 应用程序。Next.js 最大的特点是支持 服务端渲染 (SSR)静态网站生成 (SSG)客户端渲染 (CSR) 等多种渲染方式,并集成了文件系统路由、API 路由、图片优化、代码分割等诸多功能,极大地提升了开发者体验和应用性能。

核心思想:Next.js 是一个强大的 React 框架,通过多种渲染策略(SSR、SSG、CSR),优化的开发体验和内置功能,帮助开发者高效构建高性能、可扩展的现代 Web 应用。


一、为什么选择 Next.js?

React 本身是一个用于构建用户界面的库,它通常在客户端运行 (CSR)。然而,纯客户端渲染的应用存在一些缺点:

  1. SEO 不友好:搜索引擎爬虫可能无法完全索引动态生成的页面内容。
  2. 首次加载性能:用户需要等待 JavaScript 下载、解析和执行后才能看到内容,导致白屏时间较长。
  3. Bundle Size:需要将整个 React 应用的 JavaScript 打包发送到客户端。

Next.js 应运而生,旨在解决这些问题,并提供更多高级功能:

  • 多种渲染策略:提供了 SSR (Server-Side Rendering)、SSG (Static Site Generation)、ISR (Incremental Static Regeneration) 和 CSR (Client-Side Rendering) 等多种渲染方式,开发者可以根据页面需求灵活选择,优化性能和 SEO。
  • 文件系统路由:基于文件系统的直观路由,无需手动配置。
  • API 路由:允许在同一个 Next.js 项目中创建后端 API 接口,实现全栈开发。
  • 内置优化
    • 图片优化next/image 组件自动优化图片大小、格式和响应式加载。
    • 代码分割:自动将代码分割成小块,按需加载。
    • 字体优化next/font 自动优化字体加载。
    • 预渲染 (Pre-rendering):默认对页面进行预渲染,提升首屏加载速度。
  • TypeScript 支持:开箱即用,提供优秀的 TypeScript 开发体验。
  • 快速开发体验:热模块替换 (HMR)、Fast Refresh 等特性,实现秒级修改反馈。
  • 社区和生态系统:活跃的社区和丰富的插件生态。

二、Next.js 的核心特性与渲染策略

2.1 文件系统路由 (File System Routing)

Next.js 采用基于文件系统的路由方式。在 pages 目录下创建的文件或文件夹会自动映射为应用的路由。

  • pages/index.js -> /
  • pages/about.js -> /about
  • pages/posts/first-post.js -> /posts/first-post
  • 动态路由:使用方括号 [] 创建动态路由。
    • pages/posts/[id].js -> /posts/1, /posts/abc
    • 在组件中通过 useRouter().query.id 获取动态参数。
  • 捕获所有路由 (Catch-all Routes)pages/posts/[...slug].js -> /posts/a, /posts/a/b, /posts/a/b/c
  • 可选捕获所有路由 (Optional Catch-all Routes)pages/posts/[[...slug]].js -> /posts, /posts/a, /posts/a/b

示例:pages/posts/[id].js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// pages/posts/[id].js
import { useRouter } from 'next/router';

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

return (
<>
<h1>Post ID: {id}</h1>
{/* 实际项目中会根据 ID 加载文章内容 */}
</>
);
}

// 配合 getServerSideProps 或 getStaticProps
// export async function getServerSideProps(context) {
// const { id } = context.params;
// // fetch data for the post
// const res = await fetch(`https://.../api/posts/${id}`);
// const post = await res.json();
// return { props: { post } };
// }

2.2 数据获取 (Data Fetching) 与渲染策略

Next.js 提供了多种数据获取方式和渲染策略,以适应不同的应用场景。

2.2.1 客户端渲染 (Client-Side Rendering - CSR)

Next.js 页面也可以完全在客户端渲染,就像传统的 React 应用一样。这通常通过 useEffect 钩子在组件挂载后进行数据请求。

  • 优点:简单,适用于无需 SEO 且数据实时性要求高的仪表盘等应用。
  • 缺点:SEO 不友好,首次加载可能出现白屏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// components/MyComponent.js
import { useState, useEffect } from 'react';

export default function MyComponent() {
const [data, setData] = useState(null);

useEffect(() => {
fetch('/api/data')
.then((res) => res.json())
.then((d) => setData(d));
}, []);

if (!data) return <p>Loading...</p>;

return <div>{data.message}</div>;
}

2.2.2 预渲染 (Pre-rendering)

Next.js 默认会对页面进行预渲染,生成 HTML 文件,这有助于提升首次加载性能和 SEO。预渲染有两种形式:

  1. 静态网站生成 (Static Site Generation - SSG)

    • 构建时 (Build time) 生成 HTML。
    • 页面一旦生成,就会被 CDN 缓存,每次请求都直接返回静态 HTML,速度极快。
    • 适用于:营销页面、博客文章、文档等内容不常变动,或者可以提前知道所有数据的页面。
    • 实现方式:在页面组件中导出 getStaticProps 函数。
    • getStaticProps 只能在服务端运行,不会包含在客户端 bundle 中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // pages/blog/index.js
    export default function Blog({ posts }) {
    return (
    <div>
    <h1>Blog Posts</h1>
    <ul>
    {posts.map((post) => (
    <li key={post.id}>{post.title}</li>
    ))}
    </ul>
    </div>
    );
    }

    // 在构建时运行,预加载所有博客文章
    export async function getStaticProps() {
    // 模拟从 API 获取数据
    const res = await fetch('https://api.example.com/posts');
    const posts = await res.json();

    return {
    props: {
    posts, // 将数据作为 props 传递给 Blog 组件
    },
    // revalidate: 60, // 可选:ISR 功能,每60秒重新生成一次页面
    };
    }
    • SSG 配合动态路由 (getStaticPaths):对于动态路由的 SSG 页面,需要 getStaticPaths 来告诉 Next.js 需要预渲染哪些路径。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    // pages/blog/[slug].js
    export default function BlogPost({ post }) {
    if (!post) return <p>Loading...</p>; // 配合 fallback: true

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

    // 定义需要预渲染的所有 slug 路径
    export async function getStaticPaths() {
    // 模拟获取所有文章的 slug
    const res = await fetch('https://api.example.com/posts');
    const posts = await res.json();
    const paths = posts.map((post) => ({
    params: { slug: post.slug },
    }));

    return {
    paths,
    fallback: 'blocking', // 或 true / false
    // 'blocking': 新请求会等待页面生成完毕再返回。
    // 'true': 会立即返回一个空页面,客户端侧渲染,同时在后台生成新页面。
    // 'false': 任何未在 paths 中定义的路径都会返回 404。
    };
    }

    // 获取特定 slug 的文章数据
    export async function getStaticProps({ params }) {
    const res = await fetch(`https://api.example.com/posts/${params.slug}`);
    const post = await res.json();

    if (!post) {
    return { notFound: true }; // 如果数据不存在,返回 404
    }

    return {
    props: { post },
    };
    }
  2. 服务端渲染 (Server-Side Rendering - SSR)

    • 每次请求 (Per-request) 时在服务端生成 HTML。
    • 用户请求页面时,服务器动态生成包含最新数据的 HTML,并发送到浏览器。
    • 适用于:数据实时性要求高、内容频繁更新的页面(如电商产品详情页、用户个人中心)。
    • 实现方式:在页面组件中导出 getServerSideProps 函数。
    • getServerSideProps 只能在服务端运行,不会包含在客户端 bundle 中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // pages/dashboard.js
    export default function Dashboard({ userData }) {
    return (
    <div>
    <h1>Welcome, {userData.name}!</h1>
    <p>Your email: {userData.email}</p>
    </div>
    );
    }

    // 每次请求时在服务端运行
    export async function getServerSideProps(context) {
    // context 包含 req, res, query, params 等信息
    // 模拟根据用户会话获取数据
    const res = await fetch('https://api.example.com/user_data', {
    headers: {
    Cookie: context.req.headers.cookie || '', // 转发用户的 cookie
    },
    });
    const userData = await res.json();

    if (!userData) {
    return {
    redirect: {
    destination: '/login', // 未登录则重定向
    permanent: false,
    },
    };
    }

    return {
    props: { userData },
    };
    }

2.2.3 增量静态再生 (Incremental Static Regeneration - ISR)

ISR 是 SSG 的一个增强,允许在不重新构建整个应用的情况下,更新已有的静态页面。

  • 实现方式:在 getStaticProps 中添加 revalidate 属性。
  • revalidate: 60 表示如果页面在 60 秒内被访问,会返回缓存的静态页面;如果超过 60 秒后有新的请求,Next.js 会在后台重新生成该页面,并在下一次请求时返回新页面。
  • 优点:结合了 SSG 的高性能和 SSR 的数据实时性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pages/products/[id].js
// ... (组件代码与 SSG 类似) ...

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

return {
props: { product },
revalidate: 60, // 每 60 秒重新验证一次页面,如果有新请求则在后台重新生成
};
}

export async function getStaticPaths() {
// ... (获取所有产品ID,与 SSG 类似) ...
return {
paths: /* initial paths */,
fallback: 'blocking', // 或 true
};
}

2.3 API 路由 (API Routes)

Next.js 允许你在 pages/api 目录下创建后端 API 接口。

  • 文件系统路由同样适用于 API 路由。
  • pages/api/hello.js -> /api/hello
  • 这些 API 路由只在服务端运行,不会被打包到客户端。
  • 非常适合与前端页面数据获取结合,或作为轻量级的后端服务。

示例:pages/api/hello.js

1
2
3
4
5
6
7
8
9
10
11
12
13
// pages/api/hello.js
export default function handler(req, res) {
// req 是 Node.js 的 HTTP 请求对象
// res 是 Node.js 的 HTTP 响应对象
if (req.method === 'GET') {
res.status(200).json({ name: 'John Doe', message: 'Hello from API!' });
} else if (req.method === 'POST') {
res.status(200).json({ message: 'Received POST request', body: req.body });
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}

2.4 图片优化 (next/image)

Next.js 提供了 next/image 组件,可以自动优化图片,包括:

  • 按需加载 (Lazy Loading):图片只在进入视口时才加载。
  • 响应式图片:根据设备尺寸提供不同分辨率的图片。
  • 现代图片格式:自动转换为 WebP 等更高效的格式。
  • 尺寸优化:防止布局偏移。
1
2
3
4
5
6
7
8
9
10
11
12
13
import Image from 'next/image';

function MyImageComponent() {
return (
<Image
src="/images/profile.jpg"
alt="Profile Picture"
width={500} // 原始图片宽度
height={500} // 原始图片高度
priority // 可选优先级高提前加载 (LCP 优化)
/>
);
}

next/link 组件用于在应用内部进行导航。它提供了客户端路由跳转和预加载功能。

1
2
3
4
5
6
7
8
9
10
11
import Link from 'next/link';

function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/posts/123">Post 123</Link>
</nav>
);
}

Link 组件默认会在后台预加载目标页面相关的 JavaScript,使得页面切换非常流畅。

三、部署 Next.js 应用

Next.js 应用可以部署到多种平台:

  1. Vercel (推荐):Next.js 的开发公司 Vercel 提供了最无缝的部署体验,只需将代码推送到 Git 仓库(如 GitHub),Vercel 就会自动构建和部署。它针对 Next.js 进行了深度优化,支持 SSR、ISR、API 路由等所有特性。
  2. Node.js 服务器:可以将 Next.js 应用构建后部署到任何支持 Node.js 的服务器上(如 AWS EC2, DigitalOcean)。
  3. 静态文件服务器:如果应用完全采用 SSG 方式,可以通过 next build && next export 生成纯静态 HTML/CSS/JS 文件,部署到任何静态文件托管服务(如 GitHub Pages, Netlify)。
  4. Serverless Functions:Next.js 的 API 路由和 SSR 页面天然支持部署为 Serverless 函数(如 AWS Lambda, Google Cloud Functions),这在 Vercel 中是自动实现的。

四、Next.js 13/14 中的 App Router (App Directory)

Next.js 13 引入了全新的 App Router (位于 app/ 目录下),这是 Next.js 迈向 React Server Components (RSC) 的重要一步。App Router 带来了以下关键变化:

  • 默认使用 Server Componentsapp 目录下的组件默认是 React Server Components,它们在服务端渲染,不包含在客户端 bundle 中,可以显著减少客户端 JavaScript。
  • 共享布局 (Layouts):通过 layout.js 文件,可以轻松定义嵌套的共享布局。
  • 加载状态 (Loading UI):通过 loading.js 文件,可以定义每个路由段的加载状态 UI。
  • 错误边界 (Error Handling):通过 error.js 文件,可以定义每个路由段的错误 UI。
  • 流式渲染 (Streaming):结合 Server Components,可以实现更细粒度的流式渲染,先发送部分 HTML,再逐步加载其他部分。
  • 数据获取函数:不再需要 getStaticPropsgetServerSideProps。数据获取直接在 Server Components 中使用 fetch 或其他异步操作,fetch 默认会被缓存和去重。
  • Client Components (use client 指令):如果需要交互性(使用 useState, useEffect 等 React Hooks),需要将组件标记为 "use client"

App Router 示例结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app/
├── layout.js // 根布局
├── page.js // 根页面 (映射到 /)
├── dashboard/
│ ├── layout.js // Dashboard 布局
│ ├── page.js // Dashboard 页面 (映射到 /dashboard)
│ └── settings/
│ └── page.js // Settings 页面 (映射到 /dashboard/settings)
├── blog/
│ ├── [slug]/
│ │ ├── page.js // 动态博客文章页面 (映射到 /blog/my-post)
│ │ └── loading.js // 博客文章加载状态
│ └── page.js // 博客列表页面 (映射到 /blog)
└── api/
└── users/
└── route.js // API 路由 (映射到 /api/users)

app/page.js (Server Component 示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app/page.js
// 这是一个 Server Component,默认在服务器运行
export default async function HomePage() {
// 直接在 Server Component 中获取数据,默认被缓存
const res = await fetch('https://api.example.com/some-data', { next: { revalidate: 3600 } }); // 每小时重新获取
const data = await res.json();

return (
<div>
<h1>Welcome to Next.js with App Router!</h1>
<p>Data from server: {data.message}</p>
{/* <ClientComponent /> */} // 可以在 Server Component 中渲染 Client Component
</div>
);
}

components/ClientComponent.js (Client Component 示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// components/ClientComponent.js
'use client'; // 标记为 Client Component

import { useState } from 'react';

export default function ClientComponent() {
const [count, setCount] = useState(0);

return (
<div>
<p>Client side counter: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

五、总结

Next.js 是一个功能丰富、性能卓越的 React 框架,它通过提供灵活的渲染策略、内置的性能优化和友好的开发体验,极大地提升了现代 Web 应用的开发效率和用户体验。无论是需要 SEO 优化的内容网站、高性能的电商平台,还是需要全栈能力的复杂应用,Next.js 都能提供强大的支持。随着 App Router 和 React Server Components 的发展,Next.js 正在将全栈开发推向一个更加高效和智能的新阶段。