跳到内容
返回博客

2023 年 2 月 23 日,星期四

Next.js 13.2

发布者

Next.js 13.2 包括对 App Router (app) 的重大改进,为稳定性做准备

立即运行以下命令更新

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

内置 SEO 支持,带有新的 Metadata API

Next.js 从一开始就被设计为启用搜索引擎优化

提供预渲染的 HTML 内容不仅有助于改进搜索引擎的索引,还有助于提高应用程序的性能。虽然 Next.js 已经为修改应用程序中的元数据 (next/head) 提供了简单的 API 很多版本,但我们希望重新设计和增强您如何使用 App Router (app) 优化搜索引擎。

新的 Metadata API 允许您在作为服务器组件的任何布局或页面中,使用显式的元数据配置来定义元数据(例如 HTML head 元素内的 metalink 标签)。

app/layout.tsx
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
};

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

这包括对动态元数据以及静态元数据的支持

layout.js / page.js
// 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 图像

app/layout.tsx
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 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 版本中可用于 App Router (app),使用 route.ts 特殊文件。它们在 pages 目录中不可用,因为它们是 API 路由的替代品。

了解更多关于路由处理器的信息查看 API 参考。我们要感谢 SvelteKit 在此处的 先前的技术和灵感

用于服务器组件的 MDX

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

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

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

your-project/mdx-components.js
// 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-remotecontentlayer,以添加对 React 服务器组件的支持。

了解更多关于如何使用服务器组件设置 MDX 的信息部署我们的示例

Rust MDX 解析器

作为启用 MDX for Server Components 的一部分,我们还用 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}`} />
 
// ❌ TypeScript errors if href is not a valid route
<Link href="/aboot" />

此功能需要使用新的 App Router 以及 TypeScript。

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

此功能现已在 Beta 版中提供。 rewritesredirects 尚不支持。

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

改进的错误覆盖层

为了帮助提高错误的可读性和可调试性,我们对 Next.js 错误覆盖层进行了一些改进。

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

The improved error overlay in 13.2 showing version staleness.
13.2 版本中改进的错误覆盖层显示版本过时。

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

Turbopack 改进

Turbopack,在 Next.js 13 中以 alpha 版本发布,是一个增量打包器,旨在加速本地开发以及未来的生产构建。

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

  • 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': [
          {
            // Option format
            loader: '@mdx-js/loader',
            options: {
              format: 'md',
            },
          },
        ],
        '.svg': ['@svgr/webpack'],
      },
    },
  },
};

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

Webpack 风格的 Resolve 别名

现在可以配置 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 缓存(Beta 版),它是 ISR 的演进,解锁了以下功能:

  • 组件级别的渐进式 ISR
  • 无需网络请求即可更快地刷新
  • 更快地将代码更改重新部署到静态页面

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

借助 React 服务端组件的基础以及 Next.js App Router (app) 中的并置数据获取,您现在可以将静态或动态数据与其消费组件一起封装。

app/page.jsx
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 标头中,上游控制值的缓存时间。

带有 Vercel 缓存 API 的 Next.js 缓存

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 位个人开发者、谷歌和 Meta 等行业合作伙伴以及 Vercel 核心团队共同努力的成果。Next.js 每周 npm 下载量超过 390 万次,GitHub Star 数超过 10 万,是构建 Web 的最流行方式之一。

加入我们的社区: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。