2023年2月23日,星期四
Next.js 13.2
发布者Next.js 13.2 包含了对 App Router (app
) 的重大改进,为稳定性做准备
- 内置 SEO 支持: 新的 Metadata API 用于设置静态和动态
meta
标签。 - 路由处理器: 自定义请求处理器,构建于 Web
Request
和Response
之上。 - 用于服务器组件的 MDX: 在 Markdown 中使用 React 组件,仅限服务器端。
- Rust MDX 解析器: 使用全新的 Rust 插件实现更快的 Markdown 解析。
- 改进的错误覆盖层: 分离 Next.js 和 React 堆栈跟踪,以提高可读性。
- 静态类型链接(Beta 版): 使用
next/link
和 TypeScript 避免断开的链接。 - Turbopack 改进(Alpha 版): 兼容 Webpack 加载器并改进支持。
- Next.js 缓存(Beta 版): 渐进式 ISR 和更快速的代码更改重新部署。
立即运行以下命令更新
npm i next@latest react@latest react-dom@latest eslint-config-next@latest
内置 SEO 支持:使用新的 Metadata API
Next.js 从一开始就被设计为优化搜索引擎。
提供预渲染的 HTML 内容不仅有助于改进搜索引擎的索引,还可以提高应用程序的性能。虽然 Next.js 在许多版本中都提供了一个简单的 API (next/head
) 用于修改应用程序中的元数据,但我们希望重新设计和增强您如何使用 App Router (app
) 优化搜索引擎。
新的 Metadata API 允许您在任何作为服务器组件的布局或页面中,使用显式的元数据配置来定义元数据(例如 HTML head
元素内的 meta
和 link
标签)。
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
};
这个 API 简单、可组合,并且被设计为与流式服务器渲染兼容。例如,您可以在根布局中为整个应用程序设置通用的元数据属性,并为应用程序中的其他路由组合和合并元数据对象。
这包括对动态元数据以及静态元数据的支持
// Static metadata
export const metadata = {
title: '...',
};
// Dynamic metadata
export async function generateMetadata({ params, searchParams }) {
const product = await getProduct(params.id);
return { title: product.title };
}
所有元数据选项都可用,包括提供自定义元数据的能力,并通过 TypeScript 插件或添加 Metadata
类型来支持 TypeScript。
例如,您可以通过元数据定义 open graph 图像
export const metadata = {
openGraph: {
title: 'Next.js',
description: 'The React Framework for the Web',
url: 'https://nextjs.net.cn',
siteName: 'Next.js',
images: [
{
url: 'https://nextjs.net.cn/og.png',
width: 800,
height: 600,
},
],
locale: 'en-US',
type: 'website',
},
};
export default function Layout({ children }) {}
Metadata API 在 13.2 版本中可用于 App Router (app
),取代了之前的 head.js
特殊文件。它不适用于 pages
目录。
了解更多关于 SEO 的信息 或查看 Metadata 的 API 参考。我们要感谢 next-seo 在社区包方面的工作以及对初始 API 设计的反馈。
自定义路由处理器
App Router (app
) 的原始 Beta 版本缺少的功能之一是 API 路由,它存在于 pages/api
目录中。我们希望借此机会创建一个新的、更现代的 API 路由版本,该版本将深入集成到 app
的新路由系统中。
路由处理器允许您使用 Web Request 和 Response API 为给定路由创建自定义请求处理程序。
export async function GET(request: Request) {}
路由处理器具有同构 API,可无缝支持 Edge 和 Node.js 运行时,包括对流式响应的支持。由于路由处理器使用与页面和布局相同的路由段配置,因此它们支持期待已久的功能,例如通用的静态渲染和重新验证。
route.ts
文件可以导出由 HTTP 动词命名的异步函数:GET
、HEAD
、OPTIONS
、POST
、PUT
、DELETE
和 PATCH
。然后可以包装和抽象这些函数,为您的自定义路由逻辑创建助手/可重用逻辑。
其他服务器函数(如 cookies
和 headers
)可以在路由处理器内部使用——以及构建这些抽象的任何 Web API。这允许在服务器组件和路由处理器之间共享代码。
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 版本中可用于 App Router (app
),使用 route.ts
特殊文件。它们在 pages
目录中不可用,因为它们是 API 路由的替代品。
了解更多关于路由处理器的信息 或 查看 API 参考。我们要感谢 SvelteKit 在此方面的先前的技术和灵感。
用于服务器组件的 MDX
MDX 是 markdown 的超集,允许您直接在 markdown 文件中编写 JSX。它是一种强大的方式,可以在内容中添加动态交互性并嵌入 React 组件。
在 13.2 版本中,您现在可以完全使用 React 服务器组件来使用 MDX——这意味着更少的客户端 JavaScript 以实现更快的页面加载,同时保留 React 用于模板化动态 UI 的强大功能。您可以根据需要在 MDX 内容中添加交互性。
@next/mdx
插件已更新,支持新的特殊文件 mdx-components.js|ts
,该文件在应用程序的根目录中定义,以提供自定义组件
// This file allows you to provide custom React components
// to be used in MDX files. You can import and use any
// React component you want, including components from
// other libraries.
function H1({ children }) {
// ...
}
function H2({ children }) {
// ...
}
export function useMDXComponents(components) {
return { h1: H1, h2: H2, ...components };
}
此外,我们还与社区包合作,用于获取 MDX 内容 next-mdx-remote
和 contentlayer
以添加对 React 服务器组件的支持。
了解更多关于如何使用服务器组件设置 MDX 的信息 或 部署我们的示例。
Rust MDX 解析器
作为启用 MDX for Server Components 的一部分,我们还用 Rust 重写了 MDX 解析器以提高性能。与之前的基于 JavaScript 的解析器相比,这是一个显着的改进,后者在处理大量 MDX 文件时会出现明显的减速。
您可以在 next.config.js
中选择使用 Rust 解析器。例如,使用 @next/mdx
/** @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}`} />
// ❌ TypeScript errors if href is not a valid route
<Link href="/aboot" />
此功能需要使用新的 App Router 以及 TypeScript。
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
typedRoutes: true,
},
};
module.exports = nextConfig;
此功能现已在 Beta 版中提供。尚不支持 rewrites
和 redirects
。
改进的错误覆盖层
为了帮助提高错误的可读性和可调试性,我们对 Next.js 错误覆盖层进行了一些改进。
在 13.2 版本中,Next.js 和 React 堆栈跟踪现已分离,从而更容易识别错误的来源。此外,错误覆盖层现在显示 Next.js 的当前版本,帮助您了解您的版本是否为最新版本。

我们还改进了 React 水合错误的错误输出,这些错误输出现在更具可读性且更易于调试。
Turbopack 改进
Turbopack,在 Next.js 13 的 Alpha 版本中发布,是一个增量打包器,旨在加速本地开发以及未来的生产构建。
我们一直专注于在 Turbopack 中支持现有的 Next.js 功能,并在我们迈向 Beta 版的过程中提高整体稳定性。自上次发布以来,我们添加了
- 对
next/dynamic
的支持 - 对
next.config.js
中的rewrites
、redirects
、headers
和pageExtensions
的支持 - 对
pages
中的 404 和错误的支持 - 对 CSS 模块
composes: ... from ...
的支持 - 改进了 Fast Refresh 可靠性和错误恢复
- 改进了 CSS 优先级处理
- 改进了编译时评估
我们还修复了许多错误并提高了稳定性,同时通过我们一些最大的内部 Next.js 应用程序和早期的 Vercel 客户对 Turbopack 进行了内部测试。
使用 Webpack 加载器进行自定义文件转换
Turbopack 现在附带了对某些 webpack 加载器的支持和兼容性。这意味着您可以使用 Webpack 生态系统中的许多加载器将不同类型的文件转换为 JavaScript。支持 @mdx-js/loader
、@svgr/webpack
和 babel-loader
等加载器。了解更多信息 关于自定义 Turbopack 的信息。
例如,使用 experimental.turbo.loaders
为每个文件扩展名配置加载器列表
module.exports = {
experimental: {
turbo: {
loaders: {
'.md': [
{
// Option format
loader: '@mdx-js/loader',
options: {
format: 'md',
},
},
],
'.svg': ['@svgr/webpack'],
},
},
},
};
查看使用加载器的 Turbopack 示例 以获取完整示例。
Webpack 风格的 Resolve 别名
Turbopack 现在可以配置为通过别名修改模块解析,类似于 webpack 的 resolve.alias
。通过 experimental.turbo.resolveAlias
进行配置
module.exports = {
experimental: {
turbo: {
resolveAlias: {
underscore: 'lodash',
mocha: { browser: 'mocha/browser-entry.js' },
},
},
},
};
Next.js 缓存
Next.js 13.2 引入了新的 Next.js 缓存(Beta 版),它是 ISR 的演变,解锁了
- 组件级别的渐进式 ISR
- 无需网络请求即可更快地刷新
- 更快地将代码更改重新部署到静态页面
对于完全静态的页面,ISR 的工作方式与今天相同。对于具有更细粒度数据获取(混合静态和动态)的页面,Next.js 缓存使用更细粒度、短暂的缓存。
借助 React 服务器组件的基础和 Next.js App Router (app
) 中的同地数据获取,您现在可以将静态或动态数据与其消费组件一起封装。
export default async function Page() {
const [staticData, dynamicData, revalidatedData] = await Promise.all([
// Cached until manually invalidated
fetch(`https://...`),
// Refetched on every request
fetch(`https://...`, { cache: 'no-store' }),
// Cached with a lifetime of 10 seconds
fetch(`https://...`, { next: { revalidate: 10 } }),
]);
return <div>...</div>;
}
在使用 App Router 进行本地开发时,您现在将在 next dev
中看到与使用 next start
的生产环境中相同的缓存行为。当任何服务器组件或数据加载代码更改时,这会提高 Fast Refresh 的速度。
使用 Next.js 缓存,您的应用程序控制缓存——而不是第三方 API。这与 cache-control
标头不同,在 cache-control
标头中,上游控制值的缓存时间。
Next.js 缓存与 Vercel 缓存 API
Vercel 上的 Next.js 为您提供框架定义的基础设施。您编写应用程序代码,例如使用 fetch
进行组件级数据获取,我们为您搭建全局分布式基础设施,无需额外努力。
新的 Next.js 缓存使代码更改独立于数据更改。这可以大大加快静态页面的重新部署速度,因为这些页面的生成可以使用现有缓存。
这个新的 Vercel 缓存 API 旨在与任何框架一起使用,但与 Next.js 缓存具有原生集成。了解更多关于 ISR 如何演变为 Next.js 缓存的信息,以及部署到 Vercel 时 Next.js 缓存的工作原理。
自托管时的 Next.js 缓存
自托管时,使用 LRU 缓存,默认大小为 50 MB。默认情况下,缓存中的所有条目都会自动写入磁盘。如果节点具有相同的缓存键,则此文件系统缓存可以在节点之间共享,类似于 今天的 ISR 工作方式。
对于希望进一步自定义和修改 Next.js 缓存核心的开发人员,他们可以修改底层缓存键并更改缓存条目的持久化方式和位置,包括完全禁用持久化。
其他改进
- 字体: 继社区的广泛采用之后,
@next/font
现在已作为next/font
内置于 Next.js 中。这意味着您不再需要单独安装@next/font
。了解更多信息。 - 字体: 基于社区反馈,
next/font
的默认font-display
属性已从optional
更改为font-display: swap
。 - 性能: 优化了构建过程以减少内存使用,在我们的测试中节省了约 550MB (PR)。
- 性能: 避免多次加载项目配置,在我们的测试中平均加快了约 400 毫秒的构建速度 (PR)。
- 性能: 优化了错误组件,在不更改样式的情况下减少了 0.4kb 的 HTML 有效负载 (PR)。
- 性能: 将边缘捆绑包大小减少了约 130KB,几乎减半,以进一步减小部署到 Vercel 等边缘环境时的冷启动大小 (PR)。
- 安全性: 添加了配置
images.contentDispositionType: "attachment"
,以强制在直接访问 Image Optimization API 时下载图像 (PR)。
社区
Next.js 是超过 2,500 名个人开发者、Google 和 Meta 等行业合作伙伴以及我们在 Vercel 的核心团队共同努力的成果。凭借每周超过 390 万次的 npm 下载量和 100,000+ 的 GitHub 星星,Next.js 是构建 Web 的最流行方式之一。
在 GitHub Discussions、 Reddit 和 Discord 上加入社区。
本次发布由以下团队成员带来:
- Next.js 团队: Balazs、 Hannes、 Jan、 Jiachi、 Jimmy、 JJ、 Josh、 Sebastian、 Shu、 Steven、 Tim、 Wyatt 和 Andrew。
- Turbopack 团队: Alex、 Donny、 Justin、 Leah、 LongYinan、 Maia、 OJ、 Tobias 和 Will。
以及以下贡献者:@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。