跳到内容
返回博客

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

通过新的元数据 API 内置 SEO 支持

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

提供预渲染的 HTML 内容不仅有助于改善搜索引擎的索引,还可以提高应用程序的性能。虽然 Next.js 在许多版本中都为修改应用程序中的元数据 (next/head) 提供了简单的 API,但我们希望重新设计和增强您使用 App Router (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 Router (app),取代了之前的 head.js 特殊文件。它不适用于 pages 目录。

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

自定义路由处理器

App Router (app) 原始 Beta 版本缺少的一部分是 API 路由,它存在于 pages/api 目录中。我们希望借此机会创建一个新颖、更现代的 API 路由版本,并将其深度集成到 app 的新路由系统中。

路由处理器允许您使用 Web 请求响应 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 for Server Components

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 Router 以及 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 Loaders 进行自定义文件转换

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 缓存(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 生产环境相同的缓存行为。这提高了在任何服务器组件或数据加载代码更改时的快速刷新速度。

使用 Next.js 缓存,您的应用程序控制缓存——而不是第三方 API。这与 cache-control 标头不同,后者由上游控制值的缓存时间。

Next.js Cache 与 Vercel Cache API

Next.js on Vercel 为您提供框架定义的 инфраструктура。您编写应用程序代码,例如使用 fetch 进行组件级别数据获取,我们无需额外工作即可为您搭建全球分布式基础设施。

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

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

自托管时的 Next.js 缓存

在自托管时,使用 LRU 缓存,默认大小为 50MB。所有缓存条目默认自动写入磁盘。如果这些节点具有相同的缓存键,则此文件系统缓存可以在节点之间共享,类似于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",以强制在直接访问图像优化 API 时下载图像(PR)。

社区

Next.js 是 2500 多名独立开发者、Google 和 Meta 等行业合作伙伴以及 Vercel 核心团队共同努力的成果。Next.js 每周有超过 390 万次 npm 下载和 100,000 多个 GitHub 星标,是构建 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。