跳到内容
返回博客

2024 年 12 月 10 日,星期二

Next.js 15.1

发布者

Next.js 15.1 带来了核心升级、新的 API 以及开发者体验的改进。主要更新包括:

立即升级,或通过以下方式开始使用:

终端
# Use the automated upgrade CLI
npx @next/codemod@canary upgrade latest
 
# ...or upgrade manually
npm install next@latest react@latest react-dom@latest
 
# ...or start a new project
npx create-next-app@latest

React 19 (稳定版)

Next.js 15.1 现在完全支持 React 19

  • 对于 Pages Router:您现在可以使用 React 19 稳定版,而无需使用 Release Candidate 或 Canary 版本,同时继续支持 React 18。
  • 对于 App Router:我们将继续提供内置的 React Canary 版本。这些版本包括所有稳定的 React 19 更改,以及在新的 React 版本发布之前,在框架中验证的较新功能。

自 Next.js 15 版本发布以来,React 19 的一项重大新增功能是“sibling pre-warming”。

有关 React 19 更新的全面概述,请参阅 React 19 官方博客文章

改进的错误调试

我们改进了 Next.js 中的错误调试,确保您可以快速定位问题的根源,无论问题出现在终端、浏览器还是附加的调试器中。这些增强功能适用于 Webpack 和 Turbopack(Turbopack 在 Next.js 15 中已稳定)。

Source Maps 增强

通过改进 Source Maps 的使用,现在可以更轻松地追溯错误的来源。我们实现了 Source Maps 的 ignoreList 属性,这使得 Next.js 可以隐藏外部依赖项的堆栈帧,从而使您的应用程序代码成为关注的焦点。

为了更准确地映射方法名称的 Source Map,我们建议采用 Turbopack(现已稳定),它在处理和检测 Source Maps 方面比 Webpack 有所改进。

对于库作者:我们建议在发布库时填充 sourcemaps 中的 ignoreList 属性,特别是当它们被配置为外部库时(例如,在 serverExternalPackages 配置中)。

折叠的堆栈帧

我们改进了折叠堆栈帧的逻辑,以突出显示代码中最相关的部分。

  • 在浏览器和错误覆盖层中:默认情况下,来自第三方依赖项的堆栈帧被隐藏,重点关注您的应用程序代码。您可以通过单击开发者工具或覆盖层中的“显示忽略的帧”来显示隐藏的帧。
  • 在终端中:第三方依赖项帧也默认折叠,并且错误格式化现在与浏览器输出对齐,以实现一致的调试体验。错误会在浏览器中重放,以确保在您需要完整堆栈跟踪的情况下,您不会在开发期间错过重要信息。

增强的性能分析

内置浏览器性能分析器也识别忽略的堆栈帧。这使得分析您的应用程序变得更加容易,让您可以精确定位代码中的慢速函数,而不会受到外部库的干扰。

使用 Edge Runtime 改进

当使用 Edge 运行时时,错误现在在所有开发环境中都一致显示,从而确保无缝调试。以前,记录的错误只会包含消息,而不包含堆栈。

之前和之后

终端 之前

终端
  app/page.tsx (6:11) @ eval
  Error: boom
    at eval (./app/page.tsx:12:15)
    at Page (./app/page.tsx:11:74)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at stringify (<anonymous>)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
digest: "380744807"
  4 | export default function Page() {
  5 |   const throwError = myCallback(() => {
> 6 |     throw new Error('boom')
    |           ^
  7 |   }, [])
  8 |
  9 |   throwError()
 GET / 500 in 2354ms

终端 之后

终端
   Error: boom
    at eval (app/page.tsx:6:10)
    at Page (app/page.tsx:5:32)
  4 | export default function Page() {
  5 |   const throwError = myCallback(() => {
> 6 |     throw new Error('boom')
    |          ^
  7 |   }, [])
  8 |
  9 |   throwError() {
  digest: '225828171'
}

错误覆盖层 之前

An example of the Next.js error overlay before version 15.1
Next.js 15.1 版本之前的错误覆盖层示例

错误覆盖层 之后

An example of the Next.js error overlay after version 15.1
Next.js 15.1 版本之后的错误覆盖层示例

这些改进使错误更加清晰和直观,让您可以将时间集中在构建应用程序而不是调试上。

我们还很高兴地宣布,即将在未来版本中推出重新设计的错误覆盖层 UI。

after (稳定版)

after() API 在 Next.js 15 RC 首次引入后,现在已稳定。

after() 提供了一种在响应完成流式传输到用户后执行任务的方法,例如日志记录、分析和其他系统同步,而不会阻塞主响应。

主要变更

自引入以来,我们稳定了 after() 并解决了包括以下方面的反馈:

  • 改进了对自托管 Next.js 服务器的支持
  • 修复了 after() 与其他 Next.js 功能交互的场景中的 Bug
  • 增强了可扩展性,使其他平台能够注入自己的 waitUntil() 原语来驱动 after()
  • 支持运行时 API,例如服务器 Actions 和路由处理器中的 cookies()headers()
app/layout.js
import { after } from 'next/server';
import { log } from '@/app/utils';
 
export default function Layout({ children }) {
  // Secondary task
  after(() => {
    log();
  });
 
  // Primary task
  return <>{children}</>;
}

阅读文档以了解有关 after API 以及如何在其中利用它的更多信息。

forbiddenunauthorized (实验性)

Next.js 15.1 包括两个实验性 API:forbidden()unauthorized(),它们基于社区反馈。

我们希望听到您的反馈 —— 请在您的开发环境中试用并在本讨论帖中分享您的想法。

概述

如果您熟悉 App Router,您可能已经使用过 notFound() 来触发 404 行为以及可自定义的 not-found.tsx 文件。在 15.1 版本中,我们将此方法扩展到授权错误:

forbidden() 触发 403 错误,可通过 forbidden.tsx 自定义 UI。

unauthorized() 触发 401 错误,可通过 unauthorized.tsx 自定义 UI。

须知:notFound() 错误一样,如果在初始响应标头发送后触发错误,则状态代码将为 200了解更多

启用该功能

由于此功能仍处于实验阶段,您需要在 next.config.ts 文件中启用它

next.config.ts
import type { NextConfig } from 'next';
 
const nextConfig: NextConfig = {
  experimental: {
    authInterrupts: true,
  },
};
 
export default nextConfig;

注意: next.config.ts 支持在 Next.js 15 中引入。 了解更多

使用 forbidden()unauthorized()

您可以在服务器 Actions、服务器组件、客户端组件或路由处理器中使用 forbidden()unauthorized()。这是一个示例:

import { verifySession } from '@/app/lib/dal';
import { forbidden } from 'next/navigation';
 
export default async function AdminPage() {
  const session = await verifySession();
 
  // Check if the user has the 'admin' role
  if (session.role !== 'admin') {
    forbidden();
  }
 
  // Render the admin page for authorized users
  return <h1>Admin Page</h1>;
}

创建自定义错误页面

要自定义错误页面,请创建以下文件:

app/forbidden.tsx
import Link from 'next/link';
 
export default function Forbidden() {
  return (
    <div>
      <h2>Forbidden</h2>
      <p>You are not authorized to access this resource.</p>
      <Link href="/">Return Home</Link>
    </div>
  );
}
app/unauthorized.tsx
import Link from 'next/link';
 
export default function Unauthorized() {
  return (
    <div>
      <h2>Unauthorized</h2>
      <p>Please log in to access this page.</p>
      <Link href="/login">Go to Login</Link>
    </div>
  );
}

我们要感谢 Clerk 通过 PR 提出此功能,并协助我们进行 API 原型设计。在我们于 15.2 版本中稳定此功能之前,我们计划为 API 添加更多功能和改进,以支持更广泛的用例。

阅读文档以了解有关 unauthorizedforbidden API 的更多详细信息。

其他变更

  • [功能]create-next-app 中使用 ESLint 9 (PR)
  • [功能] 将最大缓存标签增加到 128 (PR)
  • [功能] 添加一个选项来禁用实验性的 CssChunkingPlugin (PR)
  • [功能] 添加实验性的 CSS 内联支持 (PR)
  • [改进] 消除 Sass legacy-js-api 警告 (PR)
  • [改进] 修复使用 rewrites 时未处理的 rejection (PR)
  • [改进] 确保在 webpack worker 失败时父进程退出 (PR)
  • [改进] 修复 catch-all 路由上的路由拦截 (PR)
  • [改进] 修复请求去重中的响应克隆问题 (PR)
  • [改进] 修复多个根布局之间的服务器 Action 重定向 (PR)
  • [改进] 支持将 MDX 插件作为字符串提供,以实现 Turbopack 兼容性 (PR)

贡献者

Next.js 是超过 3,000 位个人开发者共同努力的成果。此版本由以下人员为您带来:

非常感谢 @sokra, @molebox, @delbaoliveira, @eps1lon, @wbinnssmith, @JamBalaya56562, @hyungjikim, @adrian-faustino, @mottox2, @lubieowoce, @bgw, @mknichel, @wyattjoh, @huozhi, @kdy1, @mischnic, @ijjk, @icyJoseph, @acdlite, @unstubbable, @gaojude, @devjiwonchoi, @cena-ko, @lforst, @devpla, @samcx, @styfle, @ztanner, @Marukome0743, @timneutkens, @JeremieDoctrine, @ductnn, @karlhorky, @reynaldichernando, @chogyejin, @y-yagi, @philparzer, @alfawal, @Rhynden, @arlyon, @MJez29, @Goodosky, @themattmayfield, @tobySolutions, @kevinmitch14, @leerob, @emmanuelgautier, @mrhrifat, @lid0a, @boar-is, @nisabmohd, @PapatMayuri, @ovogmap, @Reflex2468, @LioRael, @betterthanhajin, @HerringtonDarkholme, @bpb54321, @ahmoin, @Kikobeats, @abdelrahmanAbouelkheir, @lumirlumir, @yeeed711, @petter, 和 @suu3 的帮助!