【翻】Next.js 13

为了提高英语水平和保持技术成长,开始按计划翻译一些短篇和博客,有问题欢迎讨论👻
原文:Next.js 13

正文

正如我们在 Next.js Conf 上宣布的那样,Next.js13(stable)为实现无限制的动态功能而奠定了基础:

  • app Directory(beta): 更简单,更快速,客户端 JS 内容更少
    • Layouts
    • React Server Components
    • Streaming
  • Turbopack(alpha): 基于 Rust 的 Webpack 替代品可最高提升700倍的速度。
  • New next/image: 使用本地浏览器懒加载,速度更快。
  • New @next/font(beta): 自动托管字体,布局零偏移。
  • Improved next/link: 自动<a>链接,简化API。

Next.js 和 page directory 已稳定并可用于生产。请运行

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

New app Directory(Beta)

今天,我们将改进 Next.js 中的路由和布局体验,并通过引入 app directory 和 React 的未来保持一致。这是在之前为征求社区反馈而发布的 Layouts RFC 之后的又一次改进。

app directory 目前处于 beta 阶段,我们还不建议在生产中使用。你可以在Nextjs.13中使用 pages directory 和改进的 next/image 和 next/link 组件,并根据自己的进度选择 app directory。在未来,将继续支持 pages directory。

app directory 包括以下支持:

  • Layouts: 在路由之间轻松共享用户界面,同时保留状态并避免昂贵的重新渲染。
  • Server Components: 将服务器优先作为动态应用的默认设置。
  • Streaming: 在渲染时以用户界面为单位显示即时加载状态和数据流。
  • Support for Data Fetching: async 服务器组件和扩展的 fetch API 可实现组件级数据获取。

Layouts

通过 app/ 目录,可以轻松实现布局复杂的界面,在导航过程中保持状态,避免昂贵的重新渲染,并启用高级路由模式。此外,你还可以嵌套布局,并将 app 代码与路由(如组件、tests和styles)放在一起。

app/ 内创建路由只需要一个文件,即 page.js:

// This file maps to the index route (/)
export default function Page() {
  return <h1>Hello, Next.js!</h1>;
}

你可以通过文件系统定义布局。布局在多个页面之间共享用户界面。在导航时,布局会保留状态,保持交互性,并且不会重新渲染。

export default function BlogLayout({ children }) {
  return <section>{children}</section>;
}

Server Components

app/ 目录引入了对React新服务器组件架构的支持。服务器组件和客户端组件分别在服务器和客户端充分发挥它们各自的优势,使你能够单独使用单一的编程模型构建快速、高度交互式的应用程序,提供出色的开发者体验。

在服务器组件中,我们为构建复杂的界面奠定了基础,同时减少了发送到客户端的JavaScript数量,从而加快了初始页面的加载速度。

加载路由时,将会加载 Next.js 和 React runtime, 该 runtime 可缓存且大小可预测。runtime的大小不会随着应用的增长而增加。此外,runtime 是异步加载的,因此可以在客户端逐步增强服务器上的HTML

Streaming

app/ 目录引入了逐步渲染和增量式流式传输用户界面单元到客户端的功能。

利用 Next.js 中的服务器组件和嵌套布局,你可以立即呈现页面中不需要数据的部分,并为正在获取数据的页面显示加载状态。通过这个方法,用户无需等待整个页面加载完毕,就可以开始与页面进行交互。

部署到Vercel后,使用app/目录的 Next.js 应用将默认在Node.js和Edge运行时使用流式响应,以提高性能。

Data Fetching

React最近发布的 Support for Promises RFC 为在组件内获取数据和处理Promise引入了一种强大的新方法:

async function getData() {
  const res = await fetch('https://api.example.com/...');
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
  return res.json();
}
 
// This is an async Server Component
export default async function Page() {
  const data = await getData();
 
  return <main>{/* ... */}</main>;
}

React 和 Next.js 中也扩展了本地 fetch API。它能够自动对 fetch requests 进行重复计算,并提供了一种灵活的方式在组件级获取、缓存和重新验证数据。这意味着SSG,SSR,ISR的所有优点现在都可以通过一个API获得:

// This request should be cached until manually invalidated.
// Similar to `getStaticProps`.
// `force-cache` is the default and can be omitted.
fetch(URL, { cache: 'force-cache' });
 
// This request should be refetched on every request.
// Similar to `getServerSideProps`.
fetch(URL, { cache: 'no-store' });
 
// This request should be cached with a lifetime of 10 seconds.
// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } });

在 app 目录中,你可以在layouts,pages,components中获取数据,包括支持从服务器获取流式响应。

我们正在启用符合人体工学的方式来处理加载和错误状态,并在渲染用户界面时对其进行流式处理。在未来的版本中,我们还将改进和简化数据突变。

我们很高兴能与开源社区、包维护者以及其他为 React 生态系统做出贡献的公司合作,共同打造 React 和 Next.js 的新时代。我们很高兴能在 app/ 目录中加入两个重要的社区反馈,一是在组件内部集中数据获取的能力,二是向客户端发送更少的 JavaScript 。

Introducing Turbopack (Alpha)

Next.js 包含 Turbopack, 它是基于Rust的Webpack替代品

Webpack已经被下载超过30亿次,虽然它一直是构建Web的重要组成部分,但是我们达到了基于JavaScript的工具所能达到的最高性能极限。

在Next.js12中,我们开始向Rust驱动的工具过渡。首先是迁移了Babel,转译速度提高了17倍。然后,替换了Terser,压缩速度提高了6倍。是时候全部使用原生工具进行绑定了。

在 Next.js13 中使用 Turbopack alpha 的结果是:

  • 更新速度比Webpack快700倍
  • 更新速度比Vite快10倍
  • 冷启动速度比Webpack快4倍

Turbopack 只捆绑开发中所需的最小资源,所以启动时间极短。在一个包含3000个modules的应用中,Turbopack只需1.8秒即可启动。Vite 需要11.4秒,Webpack需要16.5秒。

Turbopack开箱即用,支持服务器组件、TypeScript、JSX、CSS等。在alpha阶段,许多功能尚不支持。我们希望听到你对使用 Turbopack 快速本地迭代过程的反馈

Note: Next.js中的 Turbopack 目前只支持 next dev。查看支持的功能。我们还在努力通过 Turbopack 增加对 next build 的支持

使用next dev --turbo在 Next.js13 中试用 Turbopack alpha。

next/image

Next.js13 引入了功能强大的新Image组件,让你可以轻松显示图像而无需改变布局,并按需优化文件以提高性能。

在 Next.js 社区调查中,70%的受访者告诉我们,他们在生产中使用了 Next.js 的 Image 组件,并看到了 Core Web Vitals 的改进。在 Next.js13 中,我们将进一步改进 next/image。

新的 Image 组件:

  • 减少客户端 JavaScript
  • 更易于设计和配置
  • 默认情况下使用alt标记更易于访问
  • 与Web平台保持一致
  • 因为本地懒加载不需要 hydration,因此速度更快
import Image from 'next/image';
import avatar from './lee.png';
 
export default function Home() {
  // "alt" is now required for improved accessibility
  // optional: image files can be colocated inside the app/ directory
  return <Image alt="leeerob" src={avatar} placeholder="blur" />;
}

Upgrading next/image to Next.js 13

旧的 Image 组件已更名为 next/legacy/image。我们提供了一个codemod,它将自动把你现有的 next/image 更新为 next/legacy/image。例如,当从根目录运行此命令时,将在 ./pages 目录中运行 codemod:

npx @next/codemod next-image-to-legacy-image ./pages

@next/font

Next.js 引入了全新的字体系统:

  • 自动优化你的字体,包括自定义字体
  • 移除外部网络请求,提高私密性和性能
  • 内置自动托管功能,可托管任何字体文件
  • 使用CSS size-adjust 属性自动实现零布局偏移

新的字体系统可以让你方便的使用所有 Google 字体,同时兼顾性能和隐私。CSS和字体文件在构建时下载,并于其他静态资源一起自行托管。浏览器不会向 Google 发送任何请求。

import { Inter } from '@next/font/google';
 
const inter = Inter();
 
<html className={inter.className}></html>;

自定义字体也支持,包括支持字体文件的自动托管、缓存和预加载。

import localFont from '@next/font/local';
 
const myFont = localFont({ src: './my-font.woff2' });
 
<html className={myFont.className}></html>;

你可以自定义字体加载过程的每个部分,并且确保出色的性能和布局不变,包括 font-display,预加载,回退等。

next/link 不再需要手动添加<a>作为子代。

这个功能之前是在12.2中作为 expreimental option 添加的,现在是默认选项。在 Next.js13 中,<Link>总是渲染<a>,并允许你将props发送到底层标签上。例如:

import Link from 'next/link'
 
// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>
 
// Next.js 13: `<Link>` always renders `<a>`
<Link href="/about">
  About
</Link>

将 links 升级到 Next.js13,我们提供了一个codemod,可以自动更新 codebase。比如在根目录运行此命令,将在./pages目录中运行codemod:

npx @next/codemod new-link ./pages

OG Image Generation

社交卡片,也可称为开放图图像,可以大大提高内容点击的参与率,一些实验显示转化率最高可提高40%。

静态社交卡耗时、易出错且难维护。正因为如此,社交卡经常被忽略甚至跳过。在此之前,需要个性化和即时计算的动态社交卡既困难又昂贵。

我们创建了一个新库 @vercel/og, 可与 Next.js 无缝协作,生成动态的社交卡片。

import { ImageResponse } from '@vercel/og';
 
export const config = {
  runtime: 'experimental-edge',
};
 
export default function () {
  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
        }}
      >
        Hello, World!
      </div>
    ),
  );
}

这个方法比现有解决方法快5倍。通过使用 Vercel Edge Functions、WebAssembly 和用于将 HTML 和 CSS 转换为图像的全新核心库,并利用 React 组件抽象。

Middleware API Updates

在 Next.js12 中,我们引入了中间件,使 Next.js 路由具有充分的灵活性。然后我们听取了大家对初始 API 设计的反馈意见,并增加了一些新内容,以改善开发人员的体验,并添加了强大的新功能。

现在你可以更轻松地在请求中设置 headers:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
 
export function middleware(request: NextRequest) {
  // Clone the request headers and set a new header `x-version`
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-version', '13');
 
  // You can also set request headers in NextResponse.rewrite
  const response = NextResponse.next({
    request: {
      // New request headers
      headers: requestHeaders,
    },
  });
 
  // Set a new response header `x-version`
  response.headers.set('x-version', '13');
  return response;
}

现在,你还可以直接在中间件中提供响应,无需 rewriteredirect

import { NextRequest, NextResponse } from 'next/server';
import { isAuthenticated } from '@lib/auth';
 
// Limit the middleware to paths starting with `/api/`
export const config = {
  matcher: '/api/:function*',
};
 
export function middleware(request: NextRequest) {
  // Call our authentication function to check the request
  if (!isAuthenticated(request)) {
    // Respond with JSON indicating an error message
    return NextResponse.json(
      {
        success: false,
        message: 'Auth failed',
      },
      {
        status: 401,
      },
    );
  }
}

从中间件发送响应目前需要使用 next.config.js 中的 experimental.allowMiddlewareResponseBody 配置项。