Back返回博客

Next.js 13.2 版本发布

Next.js 13.2 为应用路由器(App Router)带来重大改进以迎接稳定版发布,包括 SEO 支持、路由处理器、服务端组件的 MDX 支持、类型安全链接等特性。

Next.js 13.2 包含针对应用路由器(app)的重大改进,为稳定版发布做好准备:

立即通过以下命令更新:

终端
npm i next@latest react@latest react-dom@latest eslint-config-next@latest

内置 SEO 支持与全新元数据 API

Next.js 从设计之初就致力于实现搜索引擎优化

提供预渲染的 HTML 内容不仅有助于改进搜索引擎索引,还能提升应用性能。虽然 Next.js 长期通过 next/head 提供简单的元数据修改 API,但我们希望为应用路由器(app)重新设计并增强 SEO 优化方式。

新的元数据 API 允许您在任意布局或页面(需为服务端组件)中通过显式配置来定义元数据(例如 HTML head 元素内的 metalink 标签)。

app/layout.tsx
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  title: '首页',
  description: '欢迎使用 Next.js',
};

该 API 简单、可组合,并设计为与流式服务端渲染兼容。例如,您可以在根布局中为整个应用设置通用元数据属性,并为应用中的其他路由组合和合并元数据对象。

这包括对动态元数据和静态元数据的支持:

layout.js / page.js
// 静态元数据
export const metadata = {
  title: '...',
};
 
// 动态元数据
export async function generateMetadata({ params, searchParams }) {
  const product = await getProduct(params.id);
  return { title: product.title };
}

所有元数据选项均可用,包括通过 TypeScript 插件或添加 Metadata 类型来支持自定义元数据。

例如,您可以通过元数据定义开放图谱图片:

app/layout.tsx
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: '面向 Web 的 React 框架',
    url: 'https://nextjs.org',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.org/og.png',
        width: 800,
        height: 600,
      },
    ],
    locale: 'en-US',
    type: 'website',
  },
};
 
export default function Layout({ children }) {}

元数据 API 在 13.2 版本中适用于应用路由器(app),取代了之前的 head.js 特殊文件。该功能不适用于 pages 目录。

了解更多关于 SEO 的信息或查看元数据 API 参考文档。我们要感谢 next-seo 社区包的工作以及对初始 API 设计的反馈。

自定义路由处理器

应用路由器(app)初始测试版缺失的功能之一是 API 路由(原位于 pages/api 目录)。我们希望借此机会创建与 app 新路由系统深度集成的现代化 API 路由版本。

路由处理器允许您使用 Web RequestResponse API 为指定路由创建自定义请求处理器。

app/example/route.ts
export async function GET(request: Request) {}

路由处理器具有同构 API,可无缝支持 Edge 和 Node.js 运行时,包括流式响应。由于路由处理器使用与页面和布局相同的路由段配置,因此支持期待已久的特性,如通用静态渲染重新验证

route.ts 文件可以导出以 HTTP 动词命名的异步函数:GETHEADOPTIONSPOSTPUTDELETEPATCH。这些函数可以被封装和抽象,为自定义路由逻辑创建辅助工具/可重用逻辑。

其他服务端函数(如 cookiesheaders)可与任何 Web API 一起在路由处理器中使用。这使得代码可以在服务端组件和路由处理器之间共享。

app/example/route.ts
import { cookies } from 'next/headers';
 
export async function GET(request: Request) {
  const cookieStore = cookies();
  const token = cookieStore.get('token');
 
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token}` },
  });
}

路由处理器在 13.2 版本中通过 route.ts 特殊文件适用于应用路由器(app)。它们不适用于 pages 目录,因为这是 API 路由的替代方案。

了解更多关于路由处理器的信息查看 API 参考文档。我们要感谢 SvelteKit 提供的先例和灵感

服务端组件的 MDX 支持

MDX 是 Markdown 的超集,允许您直接在 Markdown 文件中编写 JSX。这是在内容中添加动态交互性和嵌入 React 组件的强大方式。

在 13.2 版本中,您现在可以完全在 React 服务端组件中使用 MDX——这意味着更少的客户端 JavaScript 以实现更快的页面加载,同时保留 React 在动态 UI 模板方面的强大能力。您可以根据需要在 MDX 内容中添加交互性。

@next/mdx 插件已更新,支持在应用根目录定义新的特殊文件 mdx-components.js|ts 来提供自定义组件:

your-project/mdx-components.js
// 此文件允许您为 MDX 文件提供自定义 React 组件
// 您可以导入和使用任何 React 组件,
// 包括来自其他库的组件
function H1({ children }) {
  // ...
}
 
function H2({ children }) {
  // ...
}
 
export function useMDXComponents(components) {
  return { h1: H1, h2: H2, ...components };
}

此外,我们还与社区包 next-mdx-remotecontentlayer 合作,增加了对 React 服务端组件的支持。

了解如何设置服务端组件的 MDX部署我们的示例

Rust MDX 解析器

作为实现服务端组件 MDX 支持的一部分,我们还用 Rust 重写了 MDX 解析器以提高性能。相比之前的基于 JavaScript 的解析器,在处理大量 MDX 文件时性能有显著提升。

您可以在 next.config.js 中选择使用 Rust 解析器。例如,配合 @next/mdx

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    mdxRs: true,
  },
};
 
const withMDX = require('@next/mdx')();
module.exports = withMDX(nextConfig);

我们要感谢我们赞助的 Titus Wormer 参与此项目。如果您想在 Next.js 之外使用此功能,请查看新包 mdxjs-rs

静态类型链接

Next.js 现在可以为 app 目录中的链接提供静态类型检查,防止使用 next/link 时出现拼写错误和其他问题,从而提升页面导航时的类型安全性。

import Link from 'next/link'
 
// ✅
<Link href="/about" />
// ✅
<Link href="/blog/nextjs" />
// ✅
<Link href={`/blog/${slug}`} />
 
// ❌ 如果 href 不是有效路由,TypeScript 会报错
<Link href="/aboot" />

此功能需要使用新的应用路由器和 TypeScript。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    appDir: true,
    typedRoutes: true,
  },
};
 
module.exports = nextConfig;

该功能现已进入测试版。目前暂不支持 rewritesredirects

了解更多关于静态类型路由的信息

改进的错误覆盖层

为了提升错误信息的可读性和可调试性,我们对 Next.js 错误覆盖层进行了多项改进。

在 13.2 版本中,Next.js 和 React 的堆栈跟踪现已分离,便于识别错误来源。此外,错误覆盖层现在会显示当前 Next.js 版本,帮助您了解版本是否最新。

13.2 版本中改进的错误覆盖层显示版本过时状态

13.2 版本中改进的错误覆盖层显示版本过时状态。

我们还改进了 React 水合错误的输出,现在更易读且更易调试。

Turbopack 改进

Turbopack 作为 Next.js 13 的 Alpha 版本发布,是一个增量式打包工具,旨在加速本地开发以及未来的生产构建。

我们一直专注于在 Turbopack 中支持现有 Next.js 功能,并在迈向测试版的过程中提高整体稳定性。自上次发布以来,我们新增了:

  • 支持 next/dynamic
  • 支持 next.config.js 中的 rewritesredirectsheaderspageExtensions
  • 支持 pages 中的 404 和错误页面
  • 支持 CSS 模块的 composes: ... from ...
  • 提升 Fast Refresh 的可靠性和错误恢复能力
  • 改进 CSS 优先级处理
  • 改进编译时评估

我们还修复了许多错误,并通过在部分大型内部 Next.js 应用和早期 Vercel 客户中实际使用 Turbopack 来提高稳定性。

通过 Webpack 加载器实现自定义文件转换

Turbopack 现已支持并兼容部分 webpack 加载器。这意味着您可以使用 Webpack 生态系统中的许多加载器来将不同类型的文件转换为 JavaScript。支持 @mdx-js/loader@svgr/webpackbabel-loader 等加载器。了解更多关于自定义 Turbopack 的信息。

例如,使用 experimental.turbo.loaders 为每个文件扩展名配置加载器列表:

next.config.js
module.exports = {
  experimental: {
    turbo: {
      loaders: {
        '.md': [
          {
            // 选项格式
            loader: '@mdx-js/loader',
            options: {
              format: 'md',
            },
          },
        ],
        '.svg': ['@svgr/webpack'],
      },
    },
  },
};

查看使用加载器的 Turbopack 示例获取完整示例。

Webpack 风格的解析别名

Turbopack 现在可以通过别名配置模块解析,类似于 webpack 的 resolve.alias。通过 experimental.turbo.resolveAlias 进行配置:

next.config.js
module.exports = {
  experimental: {
    turbo: {
      resolveAlias: {
        underscore: 'lodash',
        mocha: { browser: 'mocha/browser-entry.js' },
      },
    },
  },
};

Next.js 缓存

Next.js 13.2 引入了新的 Next.js 缓存(测试版),这是 ISR 的演进,实现了:

  • 组件级渐进式 ISR
  • 无需网络请求的更快刷新
  • 更快的静态页面代码变更重新部署

对于完全静态的页面,ISR 的工作方式与当前相同。对于具有更细粒度数据获取、混合静态和动态的页面,Next.js 缓存使用更细粒度的临时缓存。

基于 React 服务端组件和 Next.js 应用路由器(app)中的数据获取共置,您现在可以将静态或动态数据与其消费组件封装在一起。

app/page.jsx
export default async function Page() {
  const [staticData, dynamicData, revalidatedData] = await Promise.all([
    // 手动失效前一直缓存
    fetch(`https://...`),
    // 每次请求都重新获取
    fetch(`https://...`, { cache: 'no-store' }),
    // 缓存生命周期为 10 秒
    fetch(`https://...`, { next: { revalidate: 10 } }),
  ]);
 
  return <div>...</div>;
}

在使用应用路由器进行本地开发时,您现在可以在 next dev 中看到与生产环境 next start 相同的缓存行为。这提高了在更改任何服务端组件或数据加载代码时的 Fast Refresh 速度。

通过 Next.js 缓存,您的应用控制缓存——而非第三方 API。这与 cache-control 标头不同,后者由上游控制缓存时长。

Next.js 缓存与 Vercel 缓存 API

Vercel 上的 Next.js 为您提供了框架定义的基础设施。您只需编写应用代码(例如使用 fetch 进行组件级数据获取),我们将自动为您搭建全球分布式基础设施,无需额外操作。

全新的 Next.js 缓存实现了代码变更与数据变更的分离。这能显著加速静态页面的重新部署,因为这些页面的生成可以利用现有缓存。

新的 Vercel 缓存 API 设计为可与任何框架协作,但与 Next.js 缓存具有原生集成。了解 ISR (增量静态再生) 如何演变为 Next.js 缓存,以及部署至 Vercel 时 Next.js 缓存的工作原理。

自托管时的 Next.js 缓存

在自托管场景下,系统默认使用容量为 50 MB 的 LRU 缓存。默认情况下所有缓存条目会自动写入磁盘。如果节点具有相同的缓存键,该文件系统缓存可在节点间共享,其机制类似于当前 ISR 的工作方式

开发者如需深度定制 Next.js 缓存核心,可修改底层缓存键,调整缓存条目的持久化方式及存储位置,甚至完全禁用持久化功能。

其他改进

  • 字体优化: 随着社区广泛采用,@next/font 现已内置为 next/font,无需单独安装。了解更多
  • 字体显示: 根据社区反馈,next/font 的默认 font-display 属性已从 optional 改为 swap
  • 性能提升: 优化构建过程内存占用,测试中节省约 550MB (PR)
  • 构建加速: 避免重复加载项目配置,测试中平均构建速度提升约 400ms (PR)
  • 负载优化: 错误组件优化后减少 0.4kb HTML 体积且不改变样式 (PR)
  • 边缘部署: 边缘包体积缩减约 130KB(近半大小),进一步降低在 Vercel 等边缘环境的冷启动耗时 (PR)
  • 安全增强: 新增 images.contentDispositionType: "attachment" 配置项,强制下载通过图片优化 API 直接访问的图像 (PR)

社区生态

Next.js 凝聚了 2,500+ 独立开发者、Google/Meta 等行业合作伙伴及 Vercel 核心团队的共同智慧。作为最流行的 Web 构建方案之一,其周均 npm 下载量超 390 万次,GitHub Star 数突破 10 万。

欢迎通过 GitHub DiscussionsRedditDiscord 加入社区。

本次版本由以下成员共同打造:

以及贡献者:@timneutkens, @loettz, @okcoker, @clive-h-townsend, @shuding, @JanKaifer, @sepiropht, @hanneslund, @huozhi, @aralroca, @balazsorban44, @cristobaldominguez95, @vinaykulk621, @Brooooooklyn, @feedthejim, @samsisle, @MarDi66, @styfle, @therealrinku, @sebmarkbage, @cravend, @hu0p, @kdy1, @ijjk, @juzhiyuan, @IvanKiral, @LukeSchlangen, @wojtekolek, @samdenty, @Josehower, @bennettdams, @SCG82, @mike-plummer, @kwonoj, @David0z, @denchance, @joulev, @wbinnssmith, @alexkirsz, @UnknownMonk, @leerob, @sairajchouhan, @imranbarbhuiya, @jomeswang, @ductnn, @thomasballinger, @chibicode, @jridgewell, @sreetamdas, @Juneezee, @SukkaW, @wyattjoh, @michaeloliverx, @cattmote, @joefreeman, @valentincostam, @qrohlf, @ossan-engineer, @rishabhpoddar, @vasucp1207, @Schniz, @andrii-bodnar, @gergelyke, @abstractvector, @wherehows, @BrodaNoel, @taep96, @abe1272001, @0xadada, @nbouvrette, @teobler, @lubakravche, @molebox, 和 @hiddenest。