跳到内容
返回博客

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 在许多版本中都提供了一个简单的 API (next/head) 用于修改应用程序中的元数据,但我们希望重新设计和增强您如何使用 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 版本中,您现在可以完全使用 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 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 标头中,上游控制值的缓存时间。

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 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。