完全掌握Next.js:从入门到理解其核心概念 – wiki基地


完全掌握Next.js:从入门到理解其核心概念

在当今Web开发的快节奏世界中,构建高性能、可扩展且用户体验出色的React应用是每个开发者追求的目标。React本身是一个优秀的UI库,但它专注于视图层,这意味着开发者需要自己解决路由、数据获取、代码分割、服务器渲染等一系列问题。这就催生了像Next.js这样的全栈React框架的出现。

Next.js,由Vercel团队开发并维护,是一个集零配置、自动化优化和全栈功能于一体的React框架。它极大地简化了React应用的开发流程,并默认集成了许多生产环境中必不可少的功能。本文将带你从Next.js的入门开始,逐步深入理解其核心概念,最终达到完全掌握的程度。

第一章:初识Next.js – 为什么选择它?

在深入技术细节之前,让我们先理解为什么越来越多的开发者选择Next.js来构建他们的React应用。传统的客户端渲染(CSR)React应用,尽管提供了出色的交互性,但也存在一些固有的挑战:

  1. SEO(搜索引擎优化)问题: 搜索引擎爬虫在抓取页面时,可能无法完全执行JavaScript来获取页面内容,导致内容无法被索引。
  2. 首屏加载性能: 用户访问应用时,需要下载、解析并执行大量的JavaScript代码后才能看到完整的页面内容,这会导致较长的白屏时间。
  3. 复杂的配置: 手动配置Webpack、Babel、React Router、服务器渲染等过程既耗时又容易出错。
  4. 全栈能力限制: 构建一个包含后端API的应用需要额外的服务器设置。

Next.js正是为了解决这些问题而生。它提供了以下核心优势:

  • 零配置/简化配置: 大部分生产所需的设置都已内置,开发者可以更快地开始项目。
  • 文件系统路由: 基于文件结构的简单路由方式,直观易懂。
  • 多种渲染策略(SSR, SSG, ISR): 根据需求选择最适合的渲染方式,优化性能和SEO。
  • 内置API路由: 允许你在同一个Next.js应用中构建后端API。
  • 代码分割: 自动按页面进行代码分割,只加载当前页面所需的代码。
  • 图像优化: 使用next/image组件自动优化图片。
  • 字体优化: 使用next/font消除布局偏移并提高隐私。
  • Fast Refresh: 开发过程中快速热更新,提升开发效率。
  • TypeScript支持: 内置并优化的TypeScript支持。

理解了Next.js的强大之处,我们就有了学习它的动力。接下来,我们将从零开始搭建第一个Next.js应用。

第二章:入门篇 – 搭建你的第一个Next.js应用

搭建Next.js应用非常简单,推荐使用官方提供的create-next-app工具链。

步骤 1:安装 Node.js

确保你的系统安装了Node.js(版本推荐 14.x 或更高)。你可以从 Node.js官网 下载安装包。

步骤 2:创建项目

打开终端,运行以下命令:

“`bash
npx create-next-app@latest my-nextjs-app –typescript –eslint

或者使用yarn

yarn create next-app my-nextjs-app –typescript –eslint

“`

这个命令会创建一个名为 my-nextjs-app 的新目录,并在其中设置好一个基础的Next.js项目。--typescript--eslint 参数分别用于添加TypeScript和ESLint支持,这是现代项目开发的推荐实践。

在安装过程中,你会看到一些配置选项,如是否使用 App Router (推荐用于新项目) 或 Pages Router。对于本文的入门和核心概念讲解,我们主要聚焦于理解 Pages Router 的核心原理(SSR, SSG, Data Fetching methods like getStaticProps/getServerSideProps),因为它更直观地展示了Next.js的渲染机制。App Router 是 Next.js 的未来方向,基于 React Server Components,会在文章末尾简要提及。选择 Pages Router 或两者都了解,都有助于理解 Next.js 的设计哲学。在本教程中,我们假定读者主要基于 Pages Router 进行学习。

选择好配置后,等待安装完成。

步骤 3:运行项目

进入项目目录:

bash
cd my-nextjs-app

运行开发服务器:

“`bash
npm run dev

或者 yarn dev

“`

开发服务器启动后,通常会在 http://localhost:3000 监听。在浏览器中打开这个地址,你将看到Next.js的默认欢迎页面。

项目结构初探

刚创建的项目目录结构大致如下:

my-nextjs-app/
├── node_modules/
├── pages/ # 核心目录:文件系统路由和API路由
│ ├── _app.tsx # 应用的入口文件,用于初始化页面
│ ├── _document.tsx # 用于自定义HTML文档(<html>, <body>等)
│ ├── api/ # API路由目录
│ │ └── hello.ts # 示例API路由
│ └── index.tsx # 首页 (对应 '/')
├── public/ # 存放静态资源(图片、字体等)
├── styles/ # 存放全局或模块化CSS样式
├── .eslintrc.json # ESLint配置文件
├── next.config.js # Next.js配置文件
├── package.json
├── README.md
├── tsconfig.json # TypeScript配置文件
└── ...

其中,pages 目录是Next.js的核心。下一章我们将深入探讨它的作用。

第三章:核心概念之一 – 文件系统路由与页面

Next.js最显著的特性之一是其基于文件系统的路由。这意味着你不需要像在使用React Router时那样手动定义路由配置。pages 目录下的每个React组件文件都会自动成为一个路由。

  • pages/index.tsx -> 对应路由 / (首页)
  • pages/about.tsx -> 对应路由 /about
  • pages/products/index.tsx -> 对应路由 /products
  • pages/products/detail.tsx -> 对应路由 /products/detail

创建新页面

尝试在 pages 目录下创建一个 contact.tsx 文件,内容如下:

“`tsx
// pages/contact.tsx
import React from ‘react’;

const ContactPage: React.FC = () => {
return (

联系我们

这是联系页面。

);
};

export default ContactPage;
“`

保存文件后,无需重启开发服务器,访问 http://localhost:3000/contact,你就能看到新创建的页面了。这就是Fast Refresh和文件系统路由协同工作带来的便利。

动态路由

除了静态路由,Next.js也支持动态路由。例如,你可能需要一个页面来显示特定商品的详情,其URL可能是 /products/123/products/abc

pages/products 目录下创建一个名为 [id].tsx 的文件:

“`tsx
// pages/products/[id].tsx
import { useRouter } from ‘next/router’;
import React from ‘react’;

const ProductDetailPage: React.FC = () => {
const router = useRouter();
const { id } = router.query; // 获取动态路由参数

return (

产品详情

产品 ID: {id}

{/ 在这里根据 id 获取并显示产品数据 /}

);
};

export default ProductDetailPage;
“`

访问 http://localhost:3000/products/123http://localhost:3000/products/abc,页面会显示对应的产品ID。方括号 [] 包围的文件名 [id] 表示这是一个动态路由参数,参数名就是方括号内的名字 (id),可以通过 useRouter().query 获取。

嵌套动态路由

你还可以创建嵌套的动态路由,例如 pages/posts/[category]/[slug].tsx 对应 /posts/technology/nextjs-guide。参数可以通过 router.query 获取,例如 { category: 'technology', slug: 'nextjs-guide' }

导航

在Next.js中,推荐使用 next/link 组件进行客户端导航,而不是传统的 <a> 标签。

“`tsx
// 在任何组件中使用
import Link from ‘next/link’;

function Navigation() {
return (

);
}
“`

next/link 组件会在后台自动预加载(prefecth)目标页面的代码,使得页面切换更加流畅。当目标链接位于视口内时,默认会自动预加载。你可以通过 prefetch 属性控制预加载行为。

第四章:核心概念之二 – 渲染策略(SSR, SSG, ISR)

这是Next.js最强大且最复杂的特性之一。理解不同的渲染策略及其适用场景,是掌握Next.js的关键。Next.js 支持以下主要渲染策略:

  1. 客户端渲染 (Client-Side Rendering – CSR): 这是React的默认渲染方式。浏览器下载JavaScript,然后在客户端执行代码生成HTML。Next.js页面默认是CSR的,除非你使用特定的数据获取函数。
  2. 服务器端渲染 (Server-Side Rendering – SSR): 页面在服务器上生成完整的HTML,然后发送给浏览器。浏览器直接显示HTML,JavaScript在后台加载并“激活”(hydration)页面,使其具有交互性。
  3. 静态站点生成 (Static Site Generation – SSG): 页面在构建时(build time)生成完整的HTML文件。这些HTML文件可以直接部署到CDN,访问速度极快,且完全无需服务器动态处理。
  4. 增量静态再生 (Incremental Static Regeneration – ISR): 在SSG的基础上,允许你在应用运行后,按需或定时重新生成(regenerate)特定的静态页面,而无需重建整个应用。

让我们详细看看SSR, SSG, 和 ISR。

服务器端渲染 (SSR)

工作原理: 当用户请求一个SSR页面时,Next.js服务器会在接收到请求后执行页面组件及其数据获取逻辑,生成HTML字符串,然后将HTML与所需的JavaScript一同发送给浏览器。浏览器渲染HTML,然后执行JavaScript,使页面可交互。

实现方式: 在页面组件中导出一个 async 函数 getServerSideProps。这个函数会在每次请求到来时在服务器上执行。

“`tsx
// pages/ssr-example.tsx
import { GetServerSideProps } from ‘next’;
import React from ‘react’;

interface SSRProps {
data: string;
timestamp: string;
}

const SSRPage: React.FC = ({ data, timestamp }) => {
return (

SSR 示例页面

数据: {data}

请求时间: {timestamp}

);
};

// 每次请求都会在服务器端运行
export const getServerSideProps: GetServerSideProps = async (context) => {
// 假设这里从API获取动态数据
const dynamicData = “这是动态获取的数据”;
const currentTime = new Date().toISOString();

// getServerSideProps 必须返回一个对象,其中包含 props 属性
return {
props: {
data: dynamicData,
timestamp: currentTime,
},
};
};

export default SSRPage;
“`

访问 http://localhost:3000/ssr-example 并刷新页面,你会发现“请求时间”会变化,证明 getServerSideProps 在每次请求时都执行了。

优点:

  • 对SEO友好:内容在服务器端生成,爬虫容易抓取。
  • 首屏加载速度快:用户能更快看到页面内容。
  • 数据实时性高:适合展示频繁变化的数据。

缺点:

  • 服务器负载较高:每次请求都需要服务器处理。
  • 性能可能受数据获取时间影响:如果数据获取慢,TTFB (Time To First Byte) 会增加。

适用场景: 需要展示频繁更新的、对SEO重要且依赖用户请求上下文(如cookie、请求头)的数据的页面,例如:用户个人主页、搜索结果页、购物车页。

静态站点生成 (SSG)

工作原理: 页面在 next build 构建命令执行时生成为静态HTML文件。生成后,这些文件可以分发到CDN。用户请求时,直接从CDN获取静态文件,速度极快。

实现方式: 在页面组件中导出一个 async 函数 getStaticProps。这个函数会在构建时在服务器上执行。

“`tsx
// pages/ssg-example.tsx
import { GetStaticProps } from ‘next’;
import React from ‘react’;

interface SSGProps {
data: string;
buildTime: string;
}

const SSGPage: React.FC = ({ data, buildTime }) => {
return (

SSG 示例页面

数据: {data}

构建时间: {buildTime}

);
};

// 在构建时运行
export const getStaticProps: GetStaticProps = async (context) => {
// 假设这里获取的数据不常变化
const staticData = “这是构建时获取的静态数据”;
const time = new Date().toISOString();

// getStaticProps 必须返回一个对象,其中包含 props 属性
return {
props: {
data: staticData,
buildTime: time,
},
};
};

export default SSGPage;
“`

运行 npm run buildyarn build,然后运行 npm startyarn start 启动生产构建的服务。访问 http://localhost:3000/ssg-example 并刷新页面,你会发现“构建时间”是固定的,因为它是在构建时确定的。

SSG 的动态路由 (getStaticPaths)

对于动态路由页面 (如 pages/products/[id].tsx),如果希望它们也是SSG,Next.js需要知道在构建时应该生成哪些路径的静态页面。这就需要使用 getStaticPaths 函数。

“`tsx
// pages/products/[id].tsx (SSG 版本)
import { GetStaticPaths, GetStaticProps } from ‘next’;
import { useRouter } from ‘next/router’;
import React from ‘react’;

interface ProductProps {
product: { id: string; name: string };
}

const ProductDetailPage: React.FC = ({ product }) => {
const router = useRouter();

// 处理 fallback 状态(如果 fallback: true)
if (router.isFallback) {
return

加载中…

;
}

return (

{product.name}

产品 ID: {product.id}

);
};

// 告诉 Next.js 在构建时应该预渲染哪些路径
export const getStaticPaths: GetStaticPaths = async () => {
// 假设从API获取所有产品ID
const productIds = [‘1’, ‘2’, ‘3’]; // 示例数据

// 将 ID 映射为 paths 数组所需的格式
const paths = productIds.map((id) => ({
params: { id: id },
}));

// 返回 paths 数组和 fallback 设置
return {
paths,
fallback: ‘blocking’, // 或 false, 或 true
};
};

// 在构建时为 getStaticPaths 返回的每个路径运行
export const getStaticProps: GetStaticProps = async ({ params }) => {
const productId = params?.id; // 获取当前路径的 ID

// 根据 ID 获取产品数据
// 假设这里是模拟的异步获取
const productData = { id: productId, name: 产品 ${productId} };

// 返回 props
return {
props: {
product: productData,
},
};
};

export default ProductDetailPage;
“`

getStaticPaths 必须返回一个包含 paths 数组的对象。paths 数组中的每个对象代表一个需要生成静态页面的动态路由路径,其 params 属性对应动态路由参数。

fallback 属性决定了当用户访问一个未在 getStaticPaths 中列出的动态路由路径时的行为:

  • fallback: false: 任何未列出的路径将返回404页面。适合只有少量已知路径的情况。
  • fallback: true: Next.js不会在构建时生成所有路径。用户首次访问未生成的路径时,会先显示一个“fallback”状态(例如加载指示器),Next.js服务器会在后台生成该页面的静态HTML,然后将生成的HTML发送给浏览器,并将其添加到静态文件中,以便后续请求直接访问静态文件。适合路径非常多但并非所有路径都会被访问,或者路径列表非常大的情况。
  • fallback: 'blocking': 类似于 fallback: true,但用户首次访问未生成的路径时,浏览器会阻塞直到页面在服务器上生成完毕并发送回来。用户不会看到“fallback”状态,而是直接看到最终页面。后续访问也是直接访问静态文件。提供了更好的用户体验,但首次访问未生成页面会慢一些。

优点:

  • 极致的性能:直接从CDN服务静态文件,加载速度极快。
  • 极高的可伸缩性:无需服务器动态处理请求,可以轻松应对大量流量。
  • 对SEO友好:内容在构建时已生成。
  • 降低服务器成本:几乎不需要服务器资源来处理请求。

缺点:

  • 数据无法实时更新:数据只能在下次构建部署后才能更新。
  • 构建时间可能很长:如果页面数量非常多,构建过程可能耗时。

适用场景: 博客文章、文档、产品目录(数据不频繁变动)、营销页面等内容相对静态且对性能和SEO要求极高的页面。

增量静态再生 (ISR)

工作原理: ISR结合了SSG的性能优势和SSR的数据更新能力。它允许在生产环境运行时,定期(或按需)重新生成已有的静态页面。当用户访问页面时,如果距离上次生成的时间超过设定的阈值,Next.js会在后台重新生成该页面,同时仍然向当前用户提供旧的(但仍然是静态的、快速的)版本。新用户或下次请求将看到更新后的页面。

实现方式:getStaticProps 返回的对象中添加 revalidate 属性,指定一个以秒为单位的时间间隔。

“`tsx
// pages/isr-example.tsx
import { GetStaticProps } from ‘next’;
import React from ‘react’;

interface ISRProps {
data: string;
timestamp: string;
}

const ISRPage: React.FC = ({ data, timestamp }) => {
return (

ISR 示例页面

数据: {data}

生成时间: {timestamp}

);
};

// 在构建时和之后每隔 revalidate 秒重新生成
export const getStaticProps: GetStaticProps = async (context) => {
console.log(‘Running getStaticProps for ISR…’); // 可以在控制台观察执行时机

// 假设这里获取的数据可能偶尔变化
const dynamicData = 这是在 ${new Date().toISOString()} 生成的数据;

return {
props: {
data: dynamicData,
timestamp: new Date().toISOString(),
},
// 每隔 10 秒检查并重新生成页面(如果期间有访问)
revalidate: 10, // 秒
};
};

export default ISRPage;
“`

运行生产构建 (npm run build, npm start)。首次访问 http://localhost:3000/isr-example,会看到一个生成时间。10秒内再次访问,时间不会变。等待超过10秒后再次访问,你看到的仍是旧时间,但Next.js会在后台触发重新生成。再次刷新页面(或新开标签页访问),你会看到新的生成时间。

ISR 的动态路由 (getStaticPaths with ISR)

动态路由页面同样可以使用ISR。getStaticPaths 的作用仍然是告诉Next.js在构建时预生成哪些路径。fallback: true'blocking' 通常与ISR结合使用,以便在构建后处理新增加的或不常访问的路径,并为它们启用ISR。

“`tsx
// pages/products/[id].tsx (SSG+ISR 版本)
import { GetStaticPaths, GetStaticProps } from ‘next’;
import { useRouter } from ‘next/router’;
import React from ‘react’;

interface ProductProps {
product: { id: string; name: string };
}

const ProductDetailPage: React.FC = ({ product }) => {
const router = useRouter();

if (router.isFallback) {
return

加载中…

;
}

return (

{product.name}

产品 ID: {product.id}

);
};

// getStaticPaths 与 SSG 类似,决定构建时预渲染哪些路径
export const getStaticPaths: GetStaticPaths = async () => {
const productIds = [‘1’, ‘2’]; // 假设只有 1 和 2 在构建时预渲染
const paths = productIds.map((id) => ({ params: { id: id } }));

return {
paths,
fallback: ‘blocking’, // 使用 fallback 处理未预渲染的路径,并为它们启用 ISR
};
};

// 为所有路径 (包括通过 fallback 访问的路径) 启用 ISR
export const getStaticProps: GetStaticProps = async ({ params }) => {
const productId = params?.id;

// 模拟数据获取
const productData = { id: productId, name: 产品 ${productId} (Updated: ${new Date().toLocaleTimeString()}) };

return {
props: {
product: productData,
},
revalidate: 10, // 每 10 秒检查更新
};
};

export default ProductDetailPage;
“`

运行生产构建。访问 /products/1 (构建时预渲染的路径) 或 /products/3 (未预渲染但通过 fallback 处理的路径)。你会看到页面数据,并可以观察到 revalidate 属性在超过指定时间后触发页面更新。

优点:

  • 结合了SSG的最佳性能和可伸缩性。
  • 数据可以相对及时地更新,无需手动重建整个应用。
  • 通过 fallback 属性,可以优雅地处理大量动态路径,甚至是在构建后新增的路径。

缺点:

  • 数据并非完全实时:用户可能在 revalidate 间隔内看到旧数据。
  • 第一次访问 revalidate 过期的页面会服务旧版本,更新在后台进行。

适用场景: 博客首页(内容列表可能变化)、电商分类页、新闻列表页等,这些页面内容需要相对频繁更新,但不需要完全实时,且对性能要求很高。

总结渲染策略

策略 数据获取时机 页面生成时机 数据新鲜度 服务器负载 首屏性能 构建时间 部署复杂度 适用场景
CSR 客户端(浏览器) 客户端(浏览器) 实时 简单 后台管理、用户操作频繁的应用
SSR 服务器端(每次请求) 服务器端(每次请求) 实时 适中 用户相关、实时性要求高、SEO重要的页面
SSG 服务器端(构建时) 服务器端(构建时) 离线/静态 极低 极快 可能慢 简单 (CDN) 博客、文档、营销页等静态或不频繁更新的内容
ISR 服务器端(构建时 + 定期/按需) 服务器端(构建时 + 定期/按需) 近实时 极快 可能慢 适中 博客列表、分类页、新闻列表等需要定期更新的内容

选择合适的渲染策略是Next.js开发中最重要的决策之一。通常一个大型应用会根据不同页面的需求混合使用这些策略。

第五章:核心概念之三 – 数据获取

在上一章中,我们已经初步接触了 getStaticPropsgetServerSideProps。它们是Next.js Pages Router 中官方推荐的用于在服务器端(构建时或每次请求时)获取数据的函数。

  • getStaticProps: 用于 SSG 和 ISR 页面。在构建时(或 ISR 的 revalidation 时)运行。无法访问请求上下文(如 Headers, Cookies)。适用于获取不依赖用户的数据。
  • getServerSideProps: 用于 SSR 页面。在每次请求时运行。可以访问请求上下文。适用于获取依赖用户或实时性要求极高的数据。

共同点:

  • 都只能在 pages 目录下的页面组件中导出。
  • 都必须是 async 函数。
  • 都必须返回一个对象,其中包含 props 属性,该属性的值会被传递给页面组件作为 props。
  • 都只在服务器端运行,不会包含在客户端打包的代码中,这意味着你可以在这些函数中使用服务器端特有的代码(如文件系统操作、直连数据库等),而无需担心它们暴露给客户端。

在组件内获取数据 (客户端获取)

虽然Next.js推崇在服务器端获取数据以提升性能和SEO,但在某些场景下,你可能仍然需要在客户端组件中通过 useEffect 或第三方库(如 SWR 或 React Query)获取数据。

“`tsx
// components/ClientDataFetcher.tsx
import React, { useState, useEffect } from ‘react’;

const ClientDataFetcher: React.FC = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchData = async () => {
try {
// 假设这里调用一个 API 路由 或外部 API
const response = await fetch(‘/api/hello’); // 示例API路由
const result = await response.json();
setData(result.name);
} catch (error) {
console.error(‘Error fetching data:’, error);
} finally {
setLoading(false);
}
};

fetchData();

}, []); // 空依赖数组表示只在组件挂载时执行一次

if (loading) {
return

加载数据中…

;
}

if (!data) {
return

数据加载失败

;
}

return (

客户端获取的数据

API 返回: {data}

);
};

export default ClientDataFetcher;
“`

这种方式的数据获取发生在浏览器端,页面会先渲染一个初始状态(可能是加载中),然后数据加载完成后更新UI。这种方式适用于:

  • 页面部分内容需要实时更新,但页面整体不需要SSR/SSG(例如一个图表组件)。
  • 数据依赖于客户端的用户交互。
  • 数据对SEO不重要。

总结数据获取:

Next.js 提供了灵活的数据获取策略。优先考虑 getStaticPropsgetServerSideProps 来实现优化的首屏加载和SEO,将大部分数据获取逻辑放在服务器端。对于需要在客户端动态加载或与用户交互相关的数据,可以使用 useEffect 或专门的客户端数据获取库。

第六章:核心概念之四 – API 路由

Next.js 允许你在 pages/api 目录下创建 API 路由。这些文件作为 Node.js 函数运行,可以像传统的后端路由一样处理 HTTP 请求。这使得你可以在同一个Next.js应用中构建前端和后端功能。

  • pages/api/hello.ts -> 对应 API 路由 /api/hello
  • pages/api/users/index.ts -> 对应 API 路由 /api/users
  • pages/api/users/[id].ts -> 对应动态 API 路由 /api/users/:id

创建 API 路由

pages/api 目录下创建一个 submit-form.ts 文件:

“`typescript
// pages/api/submit-form.ts
import type { NextApiRequest, NextApiResponse } from ‘next’;

type Data = {
message: string;
submittedData?: any; // 可选地包含提交的数据
};

// 默认导出一个请求处理函数
export default function handler(
req: NextApiRequest, // 请求对象
res: NextApiResponse // 响应对象
) {
// 只处理 POST 请求
if (req.method === ‘POST’) {
const { name, email } = req.body; // 获取请求体中的数据

// 在这里可以处理数据,例如:
// - 验证数据
// - 存入数据库
// - 调用外部服务

console.log('收到表单提交:', { name, email });

// 发送成功响应
res.status(200).json({ message: '表单提交成功!', submittedData: { name, email } });

} else {
// 处理非 POST 请求
res.setHeader(‘Allow’, [‘POST’]);
res.status(405).end(Method ${req.method} Not Allowed);
}
}
“`

这个函数接收 NextApiRequestNextApiResponse 对象作为参数。你可以从 req 对象获取请求方法、请求头、请求体等信息,使用 res 对象设置状态码、响应头和响应体。

调用 API 路由

你可以在前端代码(例如一个组件或页面)中调用这些 API 路由,就像调用任何其他后端 API 一样:

“`tsx
// components/ContactForm.tsx
import React, { useState } from ‘react’;

const ContactForm: React.FC = () => {
const [name, setName] = useState(”);
const [email, setEmail] = useState(”);
const [message, setMessage] = useState(”);
const [submitStatus, setSubmitStatus] = useState<‘idle’ | ‘submitting’ | ‘success’ | ‘error’>(‘idle’);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSubmitStatus(‘submitting’);

try {
  const response = await fetch('/api/submit-form', { // 调用 API 路由
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name, email }),
  });

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  const result = await response.json();
  setMessage(result.message);
  setSubmitStatus('success');
} catch (error) {
  console.error('提交失败:', error);
  setMessage('表单提交失败。');
  setSubmitStatus('error');
}

};

return (


setName(e.target.value)} required />

setEmail(e.target.value)} required />


{message &&

{message}

}

);
};

export default ContactForm;
“`

API 路由的用途:

  • 为前端提供数据接口。
  • 处理表单提交。
  • 与数据库交互。
  • 处理第三方服务集成(如支付、认证)。
  • 创建无服务器函数 (Serverless Functions)。部署到Vercel等平台时,API路由会自动转换为独立的Serverless Functions。

第七章:样式与静态资源

Next.js内置了对多种样式方法的支持:

  1. CSS Modules: 推荐的方式。在 .module.css 文件中定义的类名会自动具有局部作用域,避免样式冲突。
    css
    /* styles/Home.module.css */
    .container {
    padding: 0 2rem;
    }
    .title a {
    color: #0070f3;
    text-decoration: none;
    }

    “`tsx
    // pages/index.tsx
    import styles from ‘../styles/Home.module.css’;

    export default function Home() {
    return (

    Welcome to Next.js!

    );
    }
    2. **Styled JSX:** Next.js内置的零配置CSS-in-JS库。样式定义在组件内部的 `<style jsx>` 标签中。tsx
    function StyledPage() {
    return (

    Styled JSX 示例

    );
    }
    3. **全局 CSS:** 在 `pages/_app.tsx` 中导入全局 CSS 文件。tsx
    // pages/_app.tsx
    import ‘../styles/globals.css’; // 导入全局 CSS

    function MyApp({ Component, pageProps }) {
    return ;
    }

    export default MyApp;
    ``
    4. **Sass Support:** 安装
    sass后即可使用.scss.sass` 文件。
    5. Tailwind CSS: Next.js官方推荐并提供了快速集成Tailwind CSS的步骤。

静态资源

public 目录用于存放静态资源,如图片、字体、 Favicon 等。这些文件可以直接通过根路径访问,无需特殊导入。

  • public/my-image.png -> 访问路径 /my-image.png
  • public/fonts/my-font.woff2 -> 访问路径 /fonts/my-font.woff2

在代码中引用静态资源:

tsx
<img src="/my-image.png" alt="我的图片" />

第八章:内置优化

Next.js提供了几个内置组件来帮助你自动优化常见的Web性能问题。

  1. next/image: 替代标准的 <img> 标签。它会自动根据需要优化图片:

    • 自动生成不同尺寸的图片。
    • 使用现代图片格式(如 WebP)。
    • 按需加载(Lazy Loading)。
    • 防止布局偏移(Layout Shift)。

    “`tsx
    import Image from ‘next/image’;
    import profilePic from ‘../public/profile.jpg’; // 支持导入本地图片

    function ProfilePicture() {
    return (
    Profile Picture
    );
    }
    ``
    2. **
    next/font`:** 从 Next.js 13 开始引入,用于优化字体加载。它会自动处理字体文件,防止布局偏移,并确保字体在静态资源服务器上托管,提高隐私。

    “`tsx
    import { Inter } from ‘next/font/google’;

    const inter = Inter({ subsets: [‘latin’] });

    function HomePage() {
    return (

    使用优化后的字体

    这是一段文字。


    );
    }
    ``
    3. **
    next/script`:** 用于优化第三方脚本的加载策略。你可以控制脚本何时加载(例如,在页面交互之前、期间或之后)。

    “`tsx
    import Script from ‘next/script’;

    function AnalyticsPage() {
    return (
    <>

    分析页面

    滚动至顶部