Next.js 简述 - React 全栈框架

Next.js 是由 Vercel 开发的基于 React 的全栈 Web 应用框架。它提供了生产环境所需的所有功能,包括服务端渲染(SSR)、静态站点生成(SSG)、API 路由等,让开发者能够轻松构建高性能的 Web 应用。

关于路由系统 :Next.js 有两种路由系统 - Pages Router (传统方式,基于 pages/ 目录)和 App Router (Next.js 13+ 推荐,基于 app/ 目录)。本文主要介绍 App Router,这是当前推荐的方式,基于 React Server Components,提供更好的性能和开发体验。
版本说明:本文基于 Next.js 15+ 和 App Router 编写。部分特性在早期版本中可能不可用或行为不同。

Next.js 的核心功能

1. 多种渲染策略

Next.js 支持多种页面渲染策略,不同场景下性能表现不同。不同的组件可以使用不同的渲染策略,也可以混合使用。

  1. 静态站点生成 (SSG Static Site Generator): 构建时生成静态 HTML,直接提供给用户,性能最佳,默认的方式
  2. 增量静态再生成 (ISR Incremental Static Regeneration): 构建时生成静态页面,运行时可在后台定期重新生成,兼顾性能和实时性
  3. 服务端渲染 (SSR Server-Side Rendering): 每次请求时在服务器动态生成,数据最新
  4. 客户端渲染 (CSR Client-Side Rendering) : 适用于 Client Component,在浏览器中动态渲染

App Router 通过以下多种机制来决定使用哪种渲染策略,下面是常用的几种方式

  • 路由段配置 (Route Segment Config) - 如 dynamic, revalidate, fetchCache 等导出常量
  • 动态 API (Dynamic APIs) - Next.js 提供的用于访问请求时数据的异步函数,如 cookies(), headers(), connection() 等(Next.js 15+ 这些为异步函数)。使用这些 API 会让路由自动选择动态渲染(Dynamic Rendering)
  • Fetch 缓存选项 - fetch() 默认不缓存。可以通过 cache: 'force-cache' 开启缓存,或使用 next.revalidate 配置定期重新验证

2. 基于文件系统的路由

Next.js 使用基于文件系统的路由机制,无需额外配置路由。可用于 页面请求 和 API 请求

bash 复制代码
app/
  layout.js         → 根布局
  page.js           → /
  about/
    page.js         → /about
  blog/
    page.js         → /blog
    [id]/
      page.js       → /blog/:id (动态路由)
  api/
    users/
      route.js      → /api/users (API 路由)

3. 内置 CSS 支持

支持多种 CSS 方案:

  • CSS Modules
  • Sass/SCSS
  • CSS-in-JS (styled-components, emotion 等)
  • Tailwind CSS

4. 内置性能优化

Next.js 提供了多种内置的性能优化功能,开箱即用:

代码和构建优化

  • 代码分割(Code Splitting):每个页面自动分割为独立的 JavaScript 包,只加载当前页面需要的代码
  • Tree Shaking:构建时自动移除未使用的代码
  • 压缩(Minification):自动压缩 JavaScript、CSS 和 HTML
  • 预加载(Prefetching):自动预加载可见链接的页面代码,提升导航速度
  • 快速刷新(Fast Refresh):开发时修改代码后自动更新,保持组件状态,无需刷新页面

图片和字体优化

  • 图片优化Image 组件自动优化图片格式(WebP、AVIF)、尺寸和懒加载
  • 字体优化:自动内联字体 CSS,消除字体加载闪烁(FOIT/FOUT)
  • 响应式图片:根据设备自动选择合适尺寸的图片

渲染优化

  • Server Components:默认在服务器渲染,减少客户端 JavaScript 包大小
  • 流式渲染(Streaming):页面内容可以逐步发送,用户更快看到内容
  • 并行数据获取:Server Components 中可以并行获取多个数据源
  • 增量静态再生成(ISR):静态页面可以在后台定期更新,兼顾性能和实时性

缓存策略

  • 自动静态优化:没有动态数据的页面自动生成为静态 HTML
  • 全路由缓存:构建时缓存渲染结果
  • 数据缓存fetch 请求默认缓存,避免重复请求
  • 客户端路由缓存:导航时缓存页面数据,实现即时导航

性能监控

  • Web Vitals 支持:内置 Core Web Vitals 监控
  • 性能分析工具 :提供 next/bundle-analyzer 分析包大小

示例:性能对比(具体数值因应用而异)

diff 复制代码
传统 React SPA:
- 首次加载:下载全部 JS(可能较大,如 500KB+)
- 首屏渲染:空白页 → JS 加载 → React 渲染 → 显示内容(通常较慢)
- SEO:搜索引擎只能看到空白 HTML

Next.js (App Router):
- 首次加载:预渲染的 HTML + 必需的 JS(通常更小)
- 首屏渲染:立即显示内容(First Contentful Paint 更快)
- SEO:搜索引擎直接看到完整内容

关键优势:

  1. 更快的首屏渲染:用户立即看到内容
  2. 更小的 JavaScript 包:Server Components 代码不发送到客户端
  3. 更好的 SEO:搜索引擎能抓取完整的 HTML
  4. 零配置:大部分优化自动生效,无需手动配置

如何使用 Next.js

创建项目

使用 create-next-app 快速创建项目:

bash 复制代码
npx create-next-app@latest my-app
cd my-app
npm run dev

访问 http://localhost:3000 即可看到应用。

项目结构

在 App Router 中,文件名有特殊含义,下面是典型项目结构:

bash 复制代码
app/
  layout.js         → 根布局(必需)
  page.js           → 首页 (/)
  loading.js        → 加载状态
  error.js          → 错误处理
  not-found.js      → 404 页面
  about/
    page.js         → /about
  blog/
    page.js         → /blog
    [id]/
      page.js       → /blog/:id (动态路由)
  api/
    users/
      route.js      → /api/users (API 端点)
文件名 用途 说明
page.js 页面组件 定义可访问的路由,必需
layout.js 布局组件 包裹所有子路由,可嵌套
loading.js 加载状态 显示 Suspense 边界,数据加载时展示
error.js 错误处理 Error Boundary,捕获子路由错误
not-found.js 404 页面 处理找不到的路由
template.js 模板组件 类似 layout,但每次导航都重新创建实例
route.js API 路由 定义 HTTP 请求处理器 (GET/POST/PUT 等)
default.js 平行路由默认页 处理平行路由加载失败时的回退

基本页面示例

1. Server Component(默认)

默认情况下,所有组件都是 Server Component,在服务器上渲染:

javascript 复制代码
// app/posts/page.js
async function getPosts() {
  // fetch 默认不缓存,可以添加 { cache: 'force-cache' } 开启缓存
  const res = await fetch("https://api.example.com/posts");
  return res.json();
}

// 默认是 Server Component,在服务器执行
export default async function PostsPage() {
  const posts = await getPosts();
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

2. Client Component

需要交互(useState、useEffect 等)时,使用 'use client' 标记:

javascript 复制代码
// app/components/Counter.js
"use client"; // 标记为 Client Component

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}

3. 动态路由

使用 [参数名] 创建动态路由:

javascript 复制代码
// app/blog/[id]/page.js
export default async function BlogPost({ params }) {
  const { id } = params;
  const post = await fetch(`https://api.example.com/posts/${id}`).then((r) => r.json());

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

Next.js 基本原理

1. 渲染流程

Next.js 的核心是其灵活的渲染策略:

yaml 复制代码
用户请求 → Next.js Server
    ↓
判断页面类型
    ↓
├─ SSG: 返回预生成的 HTML (构建时生成)
├─ ISR: 检查缓存 → 返回缓存 / 后台重新生成
├─ SSR: 服务器渲染 → 生成 HTML → 返回
└─ CSR: 返回基础 HTML → 客户端渲染
    ↓
返回给客户端
    ↓
React Hydration (水合)
    ↓
交互式应用

2. 页面从加载到交互的完整过程

当用户访问一个 Next.js (App Router) 页面时,会经历以下阶段:

阶段 1: 服务器渲染 Server Components

javascript 复制代码
// app/blog/page.js
// ========== Server Component(只在服务器运行)==========
async function getData() {
  // 可以直接访问数据库、读取文件等
  const data = await db.query("SELECT * FROM posts");
  return data;
}

export default async function BlogPage() {
  const data = await getData();
  // 这个组件在服务器渲染完成后,
  // 只有渲染结果(HTML)会发送到客户端
  // 这段代码本身不会发送到浏览器
  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.content}</p>
      {/* 需要交互的部分使用 Client Component */}
      <LikeButton />
    </div>
  );
}

阶段 2: 生成并发送响应

服务器生成并发送:

  • Server Component 渲染后的 HTML(完整内容)
  • RSC Payload(React Server Component Payload)- 服务端组件的序列化表示
  • Client Component 的 JavaScript 代码
  • 必要的 React 运行时

阶段 3: 浏览器显示 + Hydration

javascript 复制代码
// app/components/LikeButton.js
// ========== Client Component(在浏览器运行)==========
"use client";

import { useState } from "react";

export default function LikeButton() {
  const [count, setCount] = useState(0);
  // 这段代码会发送到浏览器并执行
  return <button onClick={() => setCount(count + 1)}>点赞 {count}</button>;
}

完整时间线

css 复制代码
用户访问页面
    ↓
[服务器] 执行 Server Component (getData)
    ↓
[服务器] 渲染 Server Component → 生成 HTML
    ↓
[服务器] 生成 RSC Payload(Server Component 的序列化表示)
    ↓
[网络] 发送 HTML + RSC Payload + Client Component JS
    ↓
[浏览器] 显示 HTML(用户立即看到内容)
    ↓
[浏览器] 下载并执行 Client Component JS
    ↓
[浏览器] Hydration(激活 Client Components)
    ↓
[浏览器] 页面完全可交互(按钮可以点击)

关键优势

  • Server Component 代码不会发送到客户端:减少 JavaScript 包大小
  • 更快的首屏渲染:用户立即看到完整的 HTML
  • 更好的 SEO:搜索引擎能抓取完整内容
  • 服务器能力:可以直接访问数据库、文件系统等

3. 代码分割机制

Next.js 自动进行代码分割:

javascript 复制代码
// app/page.js
import dynamic from "next/dynamic";

// 动态导入,只在需要时加载
const HeavyComponent = dynamic(() => import("./components/Heavy"), {
  loading: () => <p>Loading...</p>,
  ssr: false, // 禁用服务端渲染
});

export default function Home() {
  return <HeavyComponent />;
}

原理

  1. 自动分割:每个页面自动成为一个代码块
  2. 动态导入 :使用 dynamic()import() 实现按需加载
  3. 预加载:Next.js 自动预加载可见链接的页面代码

4. Server Components 原理

React Server Components 是 Next.js 13+ 的核心:

arduino 复制代码
服务器                              客户端
   ↓
渲染 Server Component
   ↓
生成 RSC Payload(序列化表示)
   ↓
发送到客户端 ──────────────────→ 接收 RSC Payload
                                    ↓
                               重建 React 树
                                    ↓
                               Hydrate Client Components
                                    ↓
                               完整的交互式应用

关键优势

  • Server Component 的代码不会发送到客户端
  • 可以直接访问数据库或文件系统
  • 减少 JavaScript bundle 大小
  • 提升初始加载性能

5. 静态优化

Next.js 会自动分析页面并进行静态优化。对于不需要动态数据的页面,Next.js 会自动在构建时生成静态 HTML:

javascript 复制代码
// app/about/page.js
// 没有 fetch 或动态函数,自动静态优化
export default function About() {
  return <div>About Page</div>;
}

构建输出示例

scss 复制代码
Route (app)                              Size     First Load JS
┌ ○ /                                   1.2 kB          85 kB
├ ○ /about                              850 B           83 kB
├ ○ /blog                               2.1 kB          87 kB
├ ƒ /blog/[id]                          1.5 kB          86 kB
└ λ /dashboard                          1.8 kB          86 kB

○ (Static)   静态生成 (SSG)
ƒ (Dynamic)  动态路由 + SSG/ISR
λ (Server)   服务端渲染 (SSR)

性能优化建议

1. 选择合适的渲染策略

  • SSG: 博客、文档、营销页面(内容稳定)
  • ISR: 电商列表、新闻(内容更新频繁但可容忍延迟)
  • SSR: 个人仪表盘、实时数据(需要最新数据)
  • CSR: 交互密集型应用(如编辑器)

2. 使用 Image 组件

javascript 复制代码
import Image from "next/image";

export default function Avatar() {
  return (
    <Image
      src="/avatar.jpg"
      alt="Avatar"
      width={200}
      height={200}
      placeholder="blur" // 模糊占位符
      priority // 优先加载
    />
  );
}
javascript 复制代码
import Link from "next/link";

export default function Nav() {
  return (
    <Link href="/about" prefetch={true}>
      About
    </Link>
  );
}

生态及参考资源

Next.js 拥有丰富的生态系统:

  • Vercel: 官方托管平台,零配置部署
  • next-auth: 身份认证解决方案
  • SWR: 数据获取 Hook
  • next-i18next: 国际化
  • next-seo: SEO 优化
  • next-pwa: PWA 支持

参考

相关推荐
百花~5 小时前
前端三剑客之一 CSS~
前端·css
青天诀5 小时前
React 中 setTimeout 获取不到最新 State 的原因及解决方案
前端·react.js
拉不动的猪5 小时前
闭包实际项目中应用场景有哪些举例
前端·javascript·面试
专注前端30年5 小时前
【Vue2】基础知识汇总与实战指南
开发语言·前端·vue
懵圈5 小时前
第02章:使用Vite初始化项目
前端
CodeCraft Studio5 小时前
前端表格工具AG Grid 34.3 发布:重磅引入AI工具包,全面支持 React 19.2!
前端·人工智能·react.js·angular·ag grid·前端表格工具·透视分析
剑小麟5 小时前
maven中properties和dependencys标签的区别
java·前端·maven
西洼工作室5 小时前
优化网页性能指标:提升用户体验的关键
前端·css
掘金一周6 小时前
第一台 Andriod XR 设备发布,Jetpack Compose XR 有什么不同?对原生开发有何影响? | 掘金一周 10.30
前端·人工智能·后端