跳至内容
返回博客

2023年2月23日,星期四

Next.js 13.2

发布者

Next.js 13.2 包含对 App 路由器 (app) 的重大改进,为稳定性做好准备

通过运行以下命令立即更新

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

使用新的元数据 API 内置 SEO 支持

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

提供预渲染的 HTML 内容不仅有助于提高搜索引擎的索引效率,还可以提高应用程序的性能。虽然 Next.js 在多个版本中都提供了用于修改应用程序中元数据 (next/head) 的简单 API,但我们希望重新设计和增强您使用 App 路由器 (app) 对搜索引擎进行优化的方式。

新的元数据 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。

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

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 }) {}

元数据 API 在 13.2 中适用于 App 路由器 (app),取代了以前的 head.js 特殊文件。它不适用于 pages 目录。

详细了解 SEO 或查看元数据的 API 参考。我们要感谢next-seo 他们在社区包上的工作以及对初始 API 设计的反馈。

自定义路由处理器

App 路由器 (app) 的原始测试版发布中缺少的部分之一是 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 路由器 (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,该文件定义在应用程序的根目录下以提供自定义组件。

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 的一部分,我们还用 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 路由器以及 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 的当前版本,帮助你了解你的版本是否是最新的。

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 ... 的支持
  • 改进了快速刷新可靠性和错误恢复
  • 改进了 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 样式的解析别名

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 缓存使用更细粒度、短暂的缓存。

借助 Next.js App 路由器 (app) 中的 React 服务器组件和协同数据获取的基础,你现在可以将静态或动态数据与其使用组件一起封装。

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 路由器进行本地开发时,你现在将在 next dev 中看到与 next start 生产环境中相同的缓存行为。这提高了任何服务器组件或数据加载代码更改时的快速刷新速度。

借助 Next.js 缓存,您的应用可以控制缓存,而不是第三方 API。这与cache-control 头部不同,在 cache-control 头部中,上游控制缓存值的时长。

Next.js 缓存与 Vercel 缓存 API

Vercel 上的 Next.js 为您提供了框架定义的基础设施。您可以编写应用程序代码,例如使用fetch进行组件级数据获取,我们无需任何额外操作即可为您构建全局分布式基础设施。

新的 Next.js 缓存使代码更改与数据更改相互独立。这可以极大地加快静态页面的重新部署速度,因为这些页面的生成可以使用现有的缓存。

这个新的 Vercel 缓存 API 旨在与任何框架一起使用,但与 Next.js 缓存进行了原生集成。 了解有关 ISR 如何演变为 Next.js 缓存的更多信息,以及 Next.js 缓存部署到 Vercel 时如何工作。

自托管时的 Next.js 缓存

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

对于希望进一步自定义和修改 Next.js 缓存核心的开发人员,他们可以修改底层缓存键并更改缓存条目的持久化方式和位置,包括完全禁用持久化。

其他改进

  • 字体:在社区取得令人难以置信的采用率后,@next/font 现在已内置到 Next.js 中,成为next/font。这意味着您不再需要单独安装@next/font了解更多
  • 字体:根据社区反馈,next/font 的默认font-display 属性已更改为font-display: swap,而不是optional
  • 性能:优化了构建过程以使用更少的内存,在我们的测试中节省了约 550 MB(PR)。
  • 性能:避免多次加载项目配置,导致我们的测试中构建速度平均加快约 400 毫秒(PR)。
  • 性能:优化了错误组件,在不更改样式的情况下减少了 0.4kb 的 HTML 负载(PR)。
  • 性能:将边缘包大小减少了约 130KB,几乎减少了一半,以进一步减少部署到 Vercel 等边缘环境时的冷启动大小(PR)。
  • 安全性:添加了配置images.contentDispositionType: "attachment",以在直接访问图像优化 API 时强制下载图像(PR)。

社区

Next.js 是超过 2500 名独立开发人员、谷歌和 Meta 等行业合作伙伴以及我们在 Vercel 的核心团队共同努力的结果。Next.js 每周有超过 390 万次 npm 下载量和超过 100,000 个 GitHub 星标,是构建 Web 的最流行方式之一。

加入 GitHub 讨论RedditDiscord 中的社区。

此版本由以下人员提供

以及以下人员的贡献:@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。