likes
comments
collection
share

Next.js v15 要来了,有哪些更新?附升级指南

作者站长头像
站长
· 阅读数 29

前言

2024 年 5 月 23 日,Next.js 官方发布了 Next.js 15 RC。

所谓 RC,其实是 Release Candidate 的简写,表示正式发布之前向最终用户发布的版本。 RC 版本通常非常接近最终版本,但可能还包含一些已知的小问题。开发人员会根据用户的反馈进行最后的调整和修复,然后发布正式版本。

Next.js 15 主要带来了这些更新:

  1. React:支持 React 19 RC、React 编译器(Experimental)和水合错误改进
  2. 缓存:fetch 请求、GET 路由处理程序和客户端导航默认不再缓存
  3. Partial Prerendering (Experimental):新布局和页面配置项
  4. next/afterExperimental):新 API,在响应完成流式传输后执行代码
  5. create-next-app:新配置项,用于在本地开发中启用 Turbopack
  6. Bundling external packages(Stable):新配置项,用于 App Router 和 Pages Router

简单来说,主要有两个大更新:

  1. 支持 React 19
  2. 默认不再缓存

然后是一些新的配置项和 API。

试用 RC

如果你想体验最新的 Next.js 15 RC 版本:

npm install next@rc react@rc react-dom@rc

官方文档也做了更新,目前地址为:rc.nextjs.org/docs

接下来让我们具体看看这些更新内容吧。

1. React 相关

1.1. 支持 React 19 RC

2024 年 4 月,React 刚发布了 React 19 RC,5 月,Next.js 就发布了 Next.js 15 RC,支持了 React 19 RC,速度确实够快。

因为相互合作、联系密切、互相挖人等原因,React 团队和 Next.js 团队联系密切,目前 React 首推的生产框架便是 Next.js,Next.js 也第一时间支持 React,可以在 Next.js 中体验 React 的最新特性,可谓是强强联合。

React 19 RC 的主要更新是带来了对异步函数的支持,也就是 Actions。之前使用 React,需要手动处理挂起状态、错误、乐观更新和顺序请求,React 19 提供了 useTransition 简化了这一操作。

此外,还提供了 3 个新 hook:useOptimistic、useActionState、useFormStatus,一个新 API use,具体查看《React 19 RC》,现在你可以在 Next.js 中使用这些特性。

1.2. 支持 React Compiler

React Compiler 是 React 开发的新的实验性编译器,之前开发的时候,为了减少不必要的计算,提升应用的性能,开发者会大量使用 useMemo、useCallback 等 API 进行手动优化。React Compiler 的目的是通过对纯 JavaScript 语义和 React 规则的理解,深层次上理解代码,为代码添加自动优化功能,从而使代码更简单、更易于维护、更不易出错。

如果要体验这个功能,首先安装依赖项:

npm install babel-plugin-react-compiler

然后在 next.config.js 中添加配置项:

const nextConfig = {
  experimental: {
    reactCompiler: true,
  },
};
 
module.exports = nextConfig;

还支持配置成“选择模式”:

const nextConfig = {
  experimental: {
    reactCompiler: {
      compilationMode: 'annotation',
    },
  },
};
 
module.exports = nextConfig;

所谓“选择模式”,指的是编译器只编译带有"use memo"指令注释的组件和钩子,比如:

// src/app.jsx
export default function App() {
  "use memo";
  // ...
}

注意:annotation 模式是临时模式,旨在帮助早期使用者,React 并不打算长期使用 "use memo" 指令。

1.3. 优化水合错误改进

以前出现水合错误的提示:

Next.js v15 要来了,有哪些更新?附升级指南

现在出现水合错误的提示:

Next.js v15 要来了,有哪些更新?附升级指南

2. 缓存

缓存这个改动就大了,为了提升应用的性能,以前默认会有缓存,但缓存也会让初学者感到迷惑,为什么数据没有改变呢?小册的缓存篇也对缓存行为做了很多讲解。现在都不用担心了,因为默认取消了缓存……

具体这些缓存的取消有:

  1. fetch 请求不再缓存
  2. GET 路由处理程序默认不再缓存
  3. 客户端路由缓存默认不再缓存

2.1. fetch 默认不再缓存

首先是 fetch:

fetch('https://...', { cache: 'force-cache' | 'no-store' });

在 Next.js 14 中,如果没有配置缓存选项,默认是 force-cache,同样一个接口,如果多次调用,Next.js 会从缓存中取值,而不是每次重新获取数据。但 Next.js 15 改为了 no-store,每次都会重新调用。

如果你要为自己的接口添加缓存行为:

  1. 修改 fetch 的 cache 选项:
fetch(`https://...`, { cache: 'force-cache' })
  1. 使用 dynamic 路由配置项
// layout.js | page.js | route.js
export const dynamic = 'force-static'
  1. 使用 fetchCache 路由配置项
// layout.js | page.js | route.js
export const fetchCache = 'auto'

2.2. GET 路由处理程序默认不再缓存

然后是 GET 路由处理程序不再缓存结果。

在 Next.js v14 中,如果你只声明了一个路由的 GET 请求并且没有使用动态函数或者进行动态配置项,那么此接口的返回结果会被缓存。

export async function GET() {
  console.log('GET /api/time')
  return Response.json({ data: new Date().toLocaleTimeString() })
}

就比如在这段代码中,虽然每次返回的时间会改变,但因为结果被缓存,每次返回的结果都是一样的。对于新手,如果没仔细学习过缓存章节的内容,很容易就以为是 bug。

但是 Next.js v15 默认将不再缓存。

如果你要添加缓存行为:

export dynamic = 'force-static'

2.3. 客户端路由缓存默认不再缓存

使用 Next.js 13 之后的版本开发的 Next.js 项目,默认是 SPA 应用,即客户端拦截导航,页面不会发生刷新行为。为了让导航的体验更好,Next.js 默认会缓存客户端路由:

Next.js v15 要来了,有哪些更新?附升级指南

当我们刷新页面的时候,/about 出现了 loading 加载,当我们首次点击 Settings 导航至 /settings 的时候,也出现了 loading 加载。然而当我们再点击 About、Settings 的时候就没有 loading 加载效果了,不仅如此,查看网络请求,甚至都没有发送网络请求。

这就是客户端路由缓存的功效。尤其是搭配 <Link> 标签导航的时候,会直接从路由缓存中获取 RSC,所以当导航的时候,连时间都没有改变。

Next.js 14.2.0 提供了 staleTimes 配置项用于自定义重新验证时间。Next.js 15 依然可以用,只是将默认行为改为 staleTime: 0

如果要恢复到之前的客户端路由器缓存行为,修改 next.config.ts

const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
    },
  },
};
 
module.exports = nextConfig;

但是要注意仍然有一些行为不会改变:

  1. 前进后退的时候还是从缓存中取值(以确保浏览器可以恢复滚动位置)
  2. Loading.js 将保留缓存 5 分钟(或 staleTimes.static 配置值)。

3. Partial Prerendering 提供增量支持(Experimental)

Next.js 目前默认为静态呈现,如果使用了动态函数(如 cookies()、 headers()和未缓存的数据请求),路由改为动态渲染。所谓局部渲染(Partial Prerendering,PPR),是在同一页面上结合静态渲染和动态渲染的一种优化。

落到实际应用中,其实就是将动态 UI 包装在 Suspense 中,Next.js 会先返回静态 HTML,然后在同一 HTTP 请求中流式传输动态内容,并对之前的静态 HTML 进行替换。

其实 Next.js v14 就已经提供了 PPR,只是还处于实验中,这次更近一步,提供了增量支持配置项。使用 PPR,首先修改 next.config.ts

const nextConfig = {
  experimental: {
    ppr: 'incremental',
  },
};
 
module.exports = nextConfig;

Next.js 15 添加了 experimental_ppr 路由配置项用于将特定的布局和页面选择到 PPR 中:

// app/page.jsx
import { Suspense } from "react"
import { StaticComponent, DynamicComponent } from "@/app/ui"

export const experimental_ppr = true

export default function Page() {
  return {
    <>
      <StaticComponent />
        <Suspense fallback={...}>
        <DynamicComponent />
      </Suspense>
    </>
   };
}

如果你想设置所有页面:

const nextConfig = {
  experimental: {
    ppr: 'true',
  },
};
 
module.exports = nextConfig;

4. 新增 next/after API(Experimental)

next/after API 用于实现响应完成后的工作,比如日志记录、分析、和外部系统同步等任务。这些任务与响应没有关系,用户不需要等待它们完成。但是如果你不写在响应里,又很麻烦,因为像 Serverless 函数会在停止响应后立刻停止计算。

after 就是一个很棒的 API,它会在响应完成流式处理后安排要处理的工作,从而在不阻塞主要响应的情况下运行辅助任务,从而解决了此问题。

如果要使用 after API,首先修改 next.config.ts:

const nextConfig = {
  experimental: {
    after: true,
  },
};
 
module.exports = nextConfig;

然后在服务端组件、Server Actions、路由处理程序、中间件中都可以使用 after API:

import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
 
export default function Layout({ children }) {
  // Secondary task
  after(() => {
    log();
  });
 
  // Primary task
  return <>{children}</>;
}

next/after 具体文档地址:rc.nextjs.org/docs/app/ap…

5. create-next-app 样式修改和开发新选项

Next.js 的官方 Cli create-next-app 也进行了一些修改,比如默认页面样式改为了:

Next.js v15 要来了,有哪些更新?附升级指南

运行 create-next-app 时,会出现一个新提示,是否要启用 Turbopack 进行本地开发(默认为 No):

Would you like to use Turbopack for next dev? … No / Yes

--turbo 可用于开启 Turbopack:

npx create-next-app@rc --turbo

除此之外,还新增了一个 --empty标志,它将删除任何无关的文件和样式,从而产生最小的“hello world”页面:

npx create-next-app@rc --empty

6. Optimizing bundling of external packages

默认情况下,Next.js 会自动打包服务器组件和路由处理程序中导入的包。但如果你需要让某些包退出打包,Next.js v14 及以前需要使用 experimental.serverComponentsExternalPackages 配置项:

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Before
  experimental: {
    serverComponentsExternalPackages: ['package-name'],
  }
}
 
module.exports = nextConfig

到了 Next.js 15,升级为稳定版,并重命名为 serverExternalPackages

/** @type {import('next').NextConfig} */
const nextConfig = {
  // After
  serverExternalPackages: ['package-name'],
}
 
module.exports = nextConfig

7. v15 升级指南

安装:

npm i next@rc react@rc react-dom@rc eslint-config-next@rc

如果要缓存单个 fetch 请求:

export default async function RootLayout() {
  const a = await fetch('https://...') // Not Cached
  const b = await fetch('https://...', { cache: 'force-cache' }) // Cached
 
  // ...
}

如果要缓存多个 fetch 请求:

// Since this is the root layout, all fetch requests in the app
// that don't set their own cache option will be cached.
export const fetchCache = 'default-cache'
 
export default async function RootLayout() {
  const a = await fetch('https://...') // Cached
  const b = await fetch('https://...', { cache: 'no-store' }) // Not cached
 
  // ...
}

默认的 GET 路由处理程序如果要缓存:

export const dynamic = 'force-static'
 
export async function GET() {}

客户端路由缓存添加默认缓存时间:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
      static: 180,
    },
  },
}
 
module.exports = nextConfig

next/font 包的名字改了:

// Before
import { Inter } from '@next/font/google'
 
// After
import { Inter } from 'next/font/google'

experimental.bundlePagesExternals 已改为稳定版,重命名为 bundlePagesRouterDependencies

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Before
  experimental: {
    bundlePagesExternals: true,
  },
 
  // After
  bundlePagesRouterDependencies: true,
}
 
module.exports = nextConfig

experimental.serverComponentsExternalPackages 已改为稳定版,重命名为 serverExternalPackages

/** @type {import('next').NextConfig} */
const nextConfig = {
  // Before
  experimental: {
    serverComponentsExternalPackages: ['package-name'],
  },
 
  // After
  serverExternalPackages: ['package-name'],
}
 
module.exports = nextConfig

最后

除了这些主要的修改之外,Next.js 还有一些其他小的修改,比如 next/imagenext/font 优化等等。

总体来说,这次升级比 13 升 14 的变化要大一些。希望 Next.js 越做越好吧。

参考链接

  1. nextjs.org/blog/next-1…
  2. nextjs.org/blog/next-1…
  3. rc.nextjs.org/docs/app/ap…
  4. rc.nextjs.org/docs/app/bu…
转载自:https://juejin.cn/post/7375858343179255862
评论
请登录