跳到内容
返回博客

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 中已稳定)。

源映射增强

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

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

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

折叠的堆栈帧

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

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

增强的性能分析

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

通过 Edge Runtime 改进

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

之前和之后

终端 之前

终端
  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 功能交互的场景的 错误
  • 增强了可扩展性,使其他平台能够注入自己的 waitUntil() 原语来支持 after()
  • 支持运行时 API,例如服务器操作和路由处理程序中的 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()

您可以在服务器操作、服务器组件、客户端组件或路由处理程序中使用 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 时未处理的拒绝 (PR)
  • [改进] 确保在 webpack worker 失败时父进程退出 (PR)
  • [改进] 修复了 catch-all 路由上的路由拦截 (PR)
  • [改进] 修复了请求去重中的响应克隆问题 (PR)
  • [改进] 修复了多个根布局之间的服务器操作重定向 (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 的帮助!