Next.js 学习笔记(八)——优化

news2024/11/19 23:11:34

优化

Next.js 提供了多种内置优化,旨在提高应用程序的速度和核心网络生命指数。本指南将介绍可用于增强用户体验的优化功能。

内置组件

内置组件抽象化了实现常见 UI 优化的复杂性。这些组件包括:

  • 图像:基于本地 <img> 元素构建。图像组件通过延迟加载和根据设备大小自动调整图像大小来优化图像的性能。
  • 链接:基于本地 <a> 标签构建。链接组件在后台预取页面,以实现更快、更流畅的页面过渡。
  • 脚本:基于本地 <script> 标签构建。脚本组件使你可以控制第三方脚本的加载和执行。

元数据

元数据可帮助搜索引擎更好地理解你的内容(从而提高搜索引擎优化效果),并允许你自定义内容在社交媒体上的展示方式,帮助你在不同平台上创建更具吸引力和一致性的用户体验。

通过 Next.js 中的元数据 API,你可以修改页面的 <head> 元素。你可以通过两种方式配置元数据:

  • 基于配置的元数据:在 layout.jspage.js 文件中导出静态 metadata 对象或动态 generateMetadata 函数。
  • 基于文件的元数据:在路由段中添加静态或动态生成的特殊文件。

此外,你还可以通过 imageResponse 构造函数使用 JSX 和 CSS 创建动态 Open Graph 图片。

静态资源

Next.js /public 文件夹可用于提供静态资源,例如:图片、字体和其他文件。/public 文件夹中的文件还可由 CDN 提供商缓存,以便高效分发。

分析和监控

对于大型应用程序,Next.js 集成了流行的分析和监控工具,可帮助你了解应用程序的运行情况。了解更多信息,请参阅 OpenTelemetry 和 Instrumentation 指南。

图像

示例:

  • 图像组件

根据 Web Almanac,图像占典型网站页面权重的很大一部分,并且会对你网站的 LCP 性能产生相当大的影响。

Next.js 图像组件使用自动图像优化功能扩展了 HTML <img> 元素:

  • 尺寸优化:使用 WebP 和 AVIF 等现代图像格式,自动为每个设备提供正确尺寸的图像。
  • 视觉稳定性:防止加载图像时布局自动偏移。
  • 更快的页面加载:仅当图像使用本地浏览器延迟加载进入视口时才会加载图像,并带有可选的模糊占位符。
  • 静态资源灵活性:按需调整图像大小,即使对于存储在远程服务器上的图像也是如此

**观看:**了解更多有关如何使用 next/image → YouTube (9 minutes)。

使用方法

import Image from 'next/image'

然后,你就可以定义图片的 src(本地或远程)。

本地图像

要使用本地图像,请 import 你的 .jpg.png.webp 图像文件。

Next.js 将根据导入的文件自动确定图片的 widthheight。这些值用于防止在加载图片时发生累积布局偏移。

// app/page.js

import Image from 'next/image'
import profilePic from './me.png'
 
export default function Page() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
      // width={500} 默认提供
      // height={500} 默认提供
      // blurDataURL="data:..." 默认提供
      // placeholder="blur" // 加载时可选的模糊占位符
    />
  )
}

警告: 不支持动态 await import()require()import 必须是静态的,以便在构建时进行分析。

远程图像

要使用远程图片,src 属性应为 URL 字符串。

由于 Next.js 在构建过程中无法访问远程文件,因此需要手动提供 widthheight 和可选的 blurDataURL props。

widthheight 属性用于推断图像的正确纵横比,避免加载图像时出现布局偏移。widthheight 并不决定图像文件的渲染大小。了解有关图像大小的更多信息。

// app/page.js

import Image from 'next/image'
 
export default function Page() {
  return (
    <Image
      src="https://s3.amazonaws.com/my-bucket/profile.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}

要安全地允许优化图片,请在 next.config.js 中定义支持的 URL 模式列表。尽可能具体,以防止恶意使用。例如,以下配置只允许来自特定 AWS S3 存储桶的图片:

// next.config.js

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        port: '',
        pathname: '/my-bucket/**',
      },
    ],
  },
}

了解有关 remotePatterns 配置的更多信息。如果要为图片 src 使用相对 URL,请使用加载器

有时,你可能想优化远程图片,但仍需使用内置的 Next.js 图片优化 API。为此,请将 loader 保留为默认设置,并为图片 src prop 输入绝对 URL。

为保护应用程序不受恶意用户的攻击,必须定义一个远程主机名列表,以便与 next/image 组件一起使用。

了解有关 remotePatterns 配置的更多信息。

加载器

请注意,在前面的示例中,为本地图像提供了部分 URL("/me.png")。这是因为加载器架构的缘故。

加载器是一个为图像生成 URL 的函数。它修改所提供的 src,并生成多个 URL 以请求不同大小的图片。这些多个 URL 用于自动 srcset 生成,这样网站访问者就能获得适合其视口大小的图片。

Next.js 应用程序的默认加载器使用内置的图像优化 API,该 API 可优化网络上任何地方的图像,然后直接从 Next.js 网络服务器提供图像。如果想直接从 CDN 或图像服务器提供图像,只需编写几行 JavaScript,就能编写自己的加载器函数。

你可以使用 loader prop 定义每个图像的加载器,也可以使用 loaderFile 配置在应用程序级别定义加载器。

优先级

你应将优先级属性(priority)添加到将成为每个页面最大内容绘制(LCP)元素的图片上。这样做可以让 Next.js 特别确定图片加载的优先级(例如:通过预加载标记或优先级提示),从而有意义地提高 LCP。

LCP 元素通常是页面视口中可见的最大图像或文本块。运行 next dev 时,如果 LCP 元素是没有 priority 属性的 <Image> ,就会在控制台显示警告。

一旦确定了 LCP 图像,就可以像这样添加属性:

// app/page.js

import Image from 'next/image'
import profilePic from '../public/me.png'
 
export default function Page() {
  return <Image src={profilePic} alt="Picture of the author" priority />
}

有关优先级的更多信息,请参阅 next/image 组件文档。

图像大小

图像最常见的影响性能的方式之一是布局偏移,即图片在加载时会推移页面上的其他元素。这个性能问题让用户非常恼火,以至于有了自己的核心网页基本功能,称为累积布局偏移(Cumulative Layout Shift)。这种方式避免基于图像的布局偏移的方法是始终调整图像大小。这样,浏览器就能在加载图片前为其预留足够的空间。

由于 next/image 的设计是为了保证良好的性能效果,因此在使用时不能造成布局偏移,必须以以下三种方式之一确定大小:

  • 自动地,使用静态导入
  • 明确地,包括导入 widthheight 属性
  • 隐式地,使用填充功能,使图片展开以填充其父元素

如果我不知道图像的尺寸怎么办?

如果你在不知道图片大小的情况下从源访问图片,你可以采取以下几种方法:

  1. 使用 fill

    fill prop 允许图像按其父元素调整大小。考虑使用 CSS 在页面上沿 sizes prop 为图像的父元素提供空间,以匹配任何媒体查询断点。你还可以使用带有 fillcontaincoverobject-fit,以及 object-position 来定义图像应如何占据该空间。

  2. 对图像进行规范化

    如果要从你控制的源提供图像,请考虑修改图像管道,以将图像规范化为特定大小。

  3. 修改 API 调用

    如果你的应用程序使用 API 调用(例如:对 CMS)检索图像 URL,则可以修改 API 调用以返回图像尺寸和 URL。

如果建议的方法都无法调整图片大小,next/image 组件可以在页面上与标准 <img> 元素一起使用。

样式

图像组件的样式与普通 <img> 元素的样式类似,但有几条准则需要注意:

  • 使用 classNamestyle,而不是 styled-jsx

    • 在大多数情况下,我们建议使用 className prop。这可以是导入的 CSS 模块、全局样式表等
    • 你也可以使用 style prop 来分配内联样式
    • 你不能使用 styled-jsx,因为它的作用域是当前组件(除非你将样式标记为 global
  • 使用 fill 时,父元素必须设置 position: relative

    • 这对于在该布局模式下正确渲染图像元素是必要的
  • 使用 fill 时,父元素必须设置 display: block

    • 这是 <div> 元素的默认设置,但应另行指定

示例

响应式

在这里插入图片描述

import Image from 'next/image'
import mountains from '../public/mountains.jpg'
 
export default function Responsive() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <Image
        alt="Mountains"
        // 导入图片会自动设置宽高
        src={mountains}
        sizes="100vw"
        // 设置图像的宽度为 100%
        style={{
          width: '100%',
          height: 'auto',
        }}
      />
    </div>
  )
}
填充容器

在这里插入图片描述

import Image from 'next/image'
import mountains from '../public/mountains.jpg'
 
export default function Fill() {
  return (
    <div
      style={{
        display: 'grid',
        gridGap: '8px',
        gridTemplateColumns: 'repeat(auto-fit, minmax(400px, auto))',
      }}
    >
      <div style={{ position: 'relative', height: '400px' }}>
        <Image
          alt="Mountains"
          src={mountains}
          fill
          sizes="(min-width: 808px) 50vw, 100vw"
          style={{
            objectFit: 'cover', // cover, contain, none
          }}
        />
      </div>
      {/* 在这个容器中添加更多图像 */}
    </div>
  )
}
背景图像

在这里插入图片描述

import Image from 'next/image'
import mountains from '../public/mountains.jpg'
 
export default function Background() {
  return (
    <Image
      alt="Mountains"
      src={mountains}
      placeholder="blur"
      quality={100}
      fill
      sizes="100vw"
      style={{
        objectFit: 'cover',
      }}
    />
  )
}

有关图像组件与各种样式配合使用的示例,请参阅图像组件演示。

其他属性

查看 next/image 组件的所有属性变量。

配置

next/image 组件和 Next.js 图像优化 API 可在 next.config.js 文件中进行配置。通过这些配置,你可以启用远程图像、定义自定义图像断点、更改缓存行为等。

请阅读完整的图像配置文档,了解更多信息。

字体

next/font 将自动优化你的字体(包括自定义字体),并移除外部网络请求,以改善隐私和性能。

观看: 进一步了解如何使用 next/font → YouTube(6 分钟)。

next/font 包含内置的自动自托管功能,可用于任何字体文件。这意味着,由于使用了底层 CSS size-adjust 属性,你可以以最佳方式加载网络字体,而不会出现布局偏移。

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

谷歌字体

自动自我托管任何 Google 字体。字体包含在部署中,由与部署相同的域提供。浏览器不会向 Google 发送任何请求

next/font/google 作为函数导入你要使用的字体即可开始使用。我们建议使用可变字体以获得最佳性能和灵活性。

// app/layout.tsx

import { Inter } from 'next/font/google'
 
// 如果加载可变字体,则无需指定字体宽度
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

如果你无法使用可变字体,你需要指定宽度

// app/layout.tsx

import { Roboto } from 'next/font/google'
 
const roboto = Roboto({
  weight: '400',
  subsets: ['latin'],
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={roboto.className}>
      <body>{children}</body>
    </html>
  )
}

你可以使用数组指定多个权重和(或)样式:

// app/layout.js

const roboto = Roboto({
  weight: ['400', '700'],
  style: ['normal', 'italic'],
  subsets: ['latin'],
  display: 'swap',
})

需要知道:对于包含多个单词的字体名称,请使用下划线 (_)。例如:Roboto Mono 应导入为 Roboto_Mono

指定子集

Google 字体会自动子集。这样可以减小字体文件的大小并提高性能。你需要定义要预载的子集。如果在 preloadtrue 时未指定任何子集,将导致警告。

这可以通过在函数调用中添加来实现:

// app/layout.tsx

const inter = Inter({ subsets: ['latin'] })

更多信息,请查看字体 API 参考。

使用多种字体

你可以在应用程序中导入和使用多种字体。可以采取两种方法。

第一种方法是创建一个实用程序函数,用于导出、导入字体并在需要时应用字体的 className。这样可以确保只有在渲染时才会预载字体:

// app/fonts.ts

import { Inter, Roboto_Mono } from 'next/font/google'
 
export const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})
 
export const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
})
// app/layout.tsx

import { inter } from './fonts'
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={inter.className}>
      <body>
        <div>{children}</div>
      </body>
    </html>
  )
}
// app/page.tsx

import { roboto_mono } from './fonts'
 
export default function Page() {
  return (
    <>
      <h1 className={roboto_mono.className}>My page</h1>
    </>
  )
}

在上面的例子中,Inter 将全局应用,Roboto Mono 可根据需要导入和应用。

或者,你也可以创建一个 CSS 变量,并将其用于你喜欢的 CSS 解决方案:

// app/layout.tsx

import { Inter, Roboto_Mono } from 'next/font/google'
import styles from './global.css'
 
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
  display: 'swap',
})
 
const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  variable: '--font-roboto-mono',
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={`${inter.variable} ${roboto_mono.variable}`}>
      <body>
        <h1>My App</h1>
        <div>{children}</div>
      </body>
    </html>
  )
}
/* app/global.css */

html {
  font-family: var(--font-inter);
}
 
h1 {
  font-family: var(--font-roboto-mono);
}

在上面的示例中,Inter 将被全局应用,任何 <h1> 标签都将使用 Roboto Mono 进行样式设计。

建议: 谨慎使用多种字体,因为每一种新字体都会给客户端带来额外的下载资源。

本地字体

导入 next/font/local,并指定本地字体文件的 src。我们建议使用可变字体以获得最佳性能和灵活性。

// app/layout.tsx

import localFont from 'next/font/local'
 
// 字体文件可放置在 `app` 内
const myFont = localFont({
  src: './my-font.woff2',
  display: 'swap',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={myFont.className}>
      <body>{children}</body>
    </html>
  )
}

如果要为一个字体系列使用多个文件,src 可以是一个数组:

const roboto = localFont({
  src: [
    {
      path: './Roboto-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './Roboto-Italic.woff2',
      weight: '400',
      style: 'italic',
    },
    {
      path: './Roboto-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
    {
      path: './Roboto-BoldItalic.woff2',
      weight: '700',
      style: 'italic',
    },
  ],
})

查看字体 API 参考以了解更多信息。

使用 Tailwind CSS

next/font 可以通过 CSS 变量与 Tailwind CSS 一起使用。

在下面的例子中,我们使用了 next/font/google 中的 Inter 字体(你可以使用 Google 或本地字体中的任何字体)。使用 variable 选项加载字体,定义 CSS 变量名,并将其赋值给 inter。然后,使用 inter.variable 将 CSS 变量添加到 HTML 文档中。

// app/layout.tsx

import { Inter, Roboto_Mono } from 'next/font/google'
 
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})
 
const roboto_mono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-roboto-mono',
})
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={`${inter.variable} ${roboto_mono.variable}`}>
      <body>{children}</body>
    </html>
  )
}

最后,将 CSS 变量添加到 Tailwind CSS 配置中:

// tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
    './app/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: ['var(--font-inter)'],
        mono: ['var(--font-roboto-mono)'],
      },
    },
  },
  plugins: [],
}

现在,你可以使用 font-sansfont-mono 实用程序类将字体应用到元素中。

预加载

在网站页面上调用字体功能时,它不会在全局范围内可用,也不会在所有路由上预加载。相反,只会根据使用字体的文件类型,在相关路由上预加载字体:

  • 如果是唯一页面,则在该页面的唯一路由上预加载。
  • 如果是布局,则在该布局封装的所有路由上预加载字体。
  • 如果是根布局,则在所有路由上预加载。

重复使用字体

每次调用 localFont 或 Google 字体函数时,该字体都会在应用程序中作为一个实例托管。因此,如果在多个文件中加载相同的字体函数,就会托管同一字体的多个实例。在这种情况下,建议采取以下措施:

  • 在一个共享文件中调用字体加载器函数
  • 将其导出为常量
  • 在希望使用该字体的每个文件中导入该常量

API 参考

了解有关 next/font API 的更多信息。

脚本

布局脚本

要为多条路由加载第三方脚本,请导入 next/script 并将脚本直接包含在布局组件中:

// app/dashboard/layout.tsx

import Script from 'next/script'
 
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <section>{children}</section>
      <Script src="https://example.com/script.js" />
    </>
  )
}

当用户访问文件夹路由(例如:dashboard/page.js)或任何嵌套路由(例如:dashboard/settings/page.js)时,第三方脚本就会被获取。Next.js 将确保脚本只加载一次,即使用户在同一布局的多个路由之间导航也是如此。

应用程序脚本

要为所有路由加载第三方脚本,请导入 next/script,并将脚本直接包含在根布局中:

// app/layout.tsx

import Script from 'next/script'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}

当访问应用程序中的任何路由时,该脚本都将加载并执行。Next.js 将确保脚本只加载一次,即使用户在多个页面之间浏览也是如此。

建议: 我们建议仅在特定页面或布局中包含第三方脚本,以尽量减少对性能的不必要影响。

策略

虽然 next/script 的默认行为允许你在任何页面或布局中加载第三方脚本,但你可以使用 strategy 属性对其加载行为进行微调:

  • beforeInteractive: 在任何 Next.js 代码和页面水合之前加载脚本。
  • afterInteractive:(默认)提前加载脚本,但要在页面水合之后。
  • lazyOnload: 在浏览器空闲时稍后加载脚本。
  • worker:(实验性)在网络 Worker 中加载脚本。

请参阅 next/script API 参考文档,了解有关每种策略及其用例的更多信息。

将脚本卸载到 Web Worker(实验性)

警告worker 策略尚不稳定,不能与 app 目录一起使用。请谨慎使用。

使用 worker 策略的脚本会被卸载并在 web worker 中通过 Partytown 执行。这样可以将主线程专用于应用程序代码的其他部分,从而提高网站性能。

此策略仍处于试验阶段,只有在 next.config.js 中启用 nextScriptWorkers 标志后才能使用:

// next.config.js

module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
}

然后,运行 next(通常是 npm run devyarn dev),Next.js 就会引导你安装所需的软件包,完成设置:

npm run dev

你会看到如下说明:请运行 npm install @builder.io/partytown 来安装 Partytown。

设置完成后,定义 strategy="worker" 将自动在你的应用程序中实例化 Partytown,并将脚本卸载到网络 worker。

// pages/home.tsx

import Script from 'next/script'
 
export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}

在 web worker 中加载第三方脚本时,需要考虑一些权衡因素。请参阅 Partytown 的权衡文档获取更多信息。

内联脚本

脚本组件还支持内联脚本,即不从外部文件加载的脚本。只需将 JavaScript 放在大括号中,即可编写内联脚本:

<Script id="show-banner">
  {`document.getElementById('banner').classList.remove('hidden')`}
</Script>

或者使用 dangerousSetInnerHTML 属性:

<Script
  id="show-banner"
  dangerouslySetInnerHTML={{
    __html: `document.getElementById('banner').classList.remove('hidden')`,
  }}
/>

警告: 必须为内联脚本指定 id 属性,以便 Next.js 跟踪和优化脚本。

执行附加代码

事件处理程序可与脚本组件一起使用,以便在特定事件发生后执行附加代码:

  • onLoad:在脚本加载完成后执行代码。
  • onReady:在脚本加载完成后以及每次加载组件时执行代码。
  • onError:在脚本加载失败时执行代码。

这些处理程序只有在导入 next/script 并在客户端组件中使用时才会起作用,在客户端组件中,"use client" 被定义为第一行代码:

// app/page.tsx

'use client'
 
import Script from 'next/script'
 
export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        onLoad={() => {
          console.log('Script has loaded')
        }}
      />
    </>
  )
}

请参阅 next/script API 参考资料,了解每个事件处理程序的更多信息并查看示例。

附加属性

可以为 <script> 元素分配许多脚本组件未使用的 DOM 属性,例如:nonce 或自定义数据属性。包含任何附加属性都会自动转发到 HTML 中最终优化的 <script> 元素。

// app/page.tsx

import Script from 'next/script'
 
export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        id="example-script"
        nonce="XUENAJFW"
        data-test="script"
      />
    </>
  )
}

API 参考

了解有关 next/script API 的更多信息。

元数据

Next.js 有一个元数据 API,可用于定义应用程序元数据(例如:HTML head 元素中的 metalink 标签),以提高搜索引擎优化和网络共享性。

有两种方法可以为应用程序添加元数据:

  • 基于配置的元数据: 在 layout.jspage.js 文件中导出静态 metadata 对象或动态 generateMetadata 函数。
  • 基于文件的元数据: 为路由段添加静态或动态生成的特殊文件。

有了这两个选项,Next.js 会自动为页面生成相关的 <head> 元素。你还可以使用 ImageResponse 构造函数创建动态 OG 图像。

静态元数据

要定义静态元数据,请从 layout.js 或静态的 page.js 文件中导出一个 Metadata 对象。

// layout.tsx | page.tsx

import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: '...',
  description: '...',
}
 
export default function Page() {}

有关所有可用选项,请参阅 API 参考。

动态元数据

你可以使用 generateMetadata 函数来 fetch 需要动态值的元数据。

// app/products/[id]/page.tsx

import type { Metadata, ResolvingMetadata } from 'next'
 
type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // 读取路由 param
  const id = params.id
 
  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json())
 
  // 可选择访问和扩展(而不是替换)父元数据
  const previousImages = (await parent).openGraph?.images || []
 
  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}
 
export default function Page({ params, searchParams }: Props) {}

有关所有可用参数,请参阅 API 参考。

需要知道

  • 通过 generateMetadata 生成的静态和动态元数据仅在服务器组件中受支持
  • 对于 generateMetadatagenerateStaticParams、布局、页面和服务器组件中的相同数据,会自动对 fetch 请求进行记忆。如果无法 fetch,可以使用 React cache
  • Next.js 将等待 generateMetadata 中的数据获取完成后,再将用户界面流式传输到客户端。这将确保流式响应的第一部分包含 <head> 标记。

基于文件的元数据

这些特殊文件可用于元数据:

  • favicon.ico、apple-icon.jpg 和 icon.jpg
  • opengraph-image.jpg 和 twitter-image.jpg
  • robots.txt
  • sitemap.xml

你可以将这些文件用于静态元数据,也可以用代码编程生成这些文件。

有关实现和示例,请参阅元数据文件 API 参考和动态图像生成。

行为

基于文件的元数据具有更高的优先级,将优先于任何基于配置的元数据。

默认字段

即使路由没有定义元数据,也会添加两个默认 meta 标签:

  • 元字符集标签设置网站的字符编码。
  • 元视口标签设置网站的视口宽度和缩放比例,以便根据不同设备进行调整。
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

需要知道: 你可以覆盖默认的 viewport 元标签。

排序

元数据是按顺序评估的,从根代码段开始,直到最接近最终 page.js 代码段的代码段。例如:

  • app/layout.tsx(根布局)
  • app/blog/layout.tsx(嵌套博客布局)
  • app/blog/[slug]/page.tsx (博客页面)
合并

按照评估顺序,从同一路由的多个片段中导出的元数据对象会浅层合并在一起,形成路由的最终元数据输出。重复的键会根据其排序进行替换

这意味着带有嵌套字段(例如:openGraphrobots)的元数据,如果在较早的分段中定义,则会被最后一个定义它们的分段覆盖

覆盖字段
// app/layout.js

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
// app/blog/page.js

export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}
 
// 输出:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

在上面的示例中:

  • app/layout.js 中的 title 会被 app/blog/page.js 中的 title替换
  • 由于 app/blog/page.js 设置了 openGraph 元数据,因此 app/layout.js 中的所有 openGraph 字段都会在 app/blog/page.js 中被替换。请注意这里没有 openGraph.description

如果想在片段之间共享某些嵌套字段,同时覆盖其他字段,可以将它们提取到一个单独的变量中:

// app/shared-metadata.js

export const openGraphImage = { images: ['http://...'] }
// app/page.js

import { openGraphImage } from './shared-metadata'
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
// app/about/page.js

import { openGraphImage } from '../shared-metadata'
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

在上例中,app/layout.jsapp/about/page.js 共享 OG 图像,而标题则不同。

继承字段
// app/layout.js

export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
// app/about/page.js

export const metadata = {
  title: 'About',
}
 
// 输出:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

注释

  • app/about/page.js 中的 title 将被 app/about/page.js 中的 title 替换
  • app/about/page.js 继承app/layout.js 中的所有 openGraph 字段,因为 app/about/page.js 不设置 openGraph 元数据。

动态图像生成

ImageResponse 构造函数允许你使用 JSX 和 CSS 生成动态图像。这对于创建 Open Graph 图像、Twitter 卡片等社交媒体图像非常有用。

ImageResponse 使用 Edge 运行时,Next.js 会自动将正确的头添加到 edge 缓存的图片中,从而帮助提高性能并减少重新计算。

要使用它,可以从 next/og 导入 ImageResponse

// app/about/route.js

import { ImageResponse } from 'next/og'
 
export const runtime = 'edge'
 
export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}

ImageResponse 可与其他 Next.js API(包括路由处理程序和基于文件的元数据)很好地集成。例如,你可以在 opengraph-image.tsx 文件中使用 ImageResponse,在构建时生成 Open Graph 图像,或在请求时动态生成 Open Graph 图像。

ImageResponse 支持常见的 CSS 属性,包括 Flex 盒和绝对定位、自定义字体、文本换行、居中和嵌套图片。查看支持的 CSS 属性的完整列表。

需要知道

  • 可在 Vercel OG Playground 中查看示例
  • ImageResponse 使用 @vercel/og、Satori和 Resvg 将 HTML 和 CSS 转换为 PNG。
  • 仅支持 Edge 运行时。默认的 Node.js 运行时不起作用。
  • 仅支持 Flex 盒和 CSS 属性子集。高级布局(例如:display: grid)将无法使用。
  • 软件包最大容量为 500KB。捆绑包大小包括 JSX、CSS、字体、图像和任何其他资源。如果超出限制,请考虑减小任何资源的大小或在运行时获取。
  • 仅支持 ttfotfwoff 字体格式。为了最大限度地提高字体解析速度,ttfotfwoff 更受青睐。

JSON-LD

JSON-LD 是一种结构化数据格式,搜索引擎可以用它来理解你的内容。例如:你可以用它来描述一个人、一个事件、一个组织、一部电影、一本书、一份食谱以及许多其他类型的实体。

对于 JSON-LD,我们目前的建议是在 layout.jspage.js 组件中以 <script> 标签的形式渲染结构化数据。例如:

// app/products/[id]/page.tsx

export default async function Page({ params }) {
  const product = await getProduct(params.id)
 
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    image: product.image,
    description: product.description,
  }
 
  return (
    <section>
      {/* 为你的页面添加 JSON-LD */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

你可以使用针对 Google 的 Rich Results Test 或通用的 Schema Markup Validator。

你可以使用 TypeScript 社区包(例如:schema-dts)来键入 JSON-LD。

import { Product, WithContext } from 'schema-dts'
 
const jsonLd: WithContext<Product> = {
  '@context': 'https://schema.org',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: 'https://nextjs.org/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}

静态资源

Next.js 可以在根目录中名为 public 的文件夹下提供静态文件(例如:图片)。然后,你的代码就可以从基本 URL (/) 开始引用 public 文件夹中的文件。

例如,如果在 public 文件夹中添加 me.png,下面的代码就可以访问该图片:

// Avatar.js

import Image from 'next/image'
 
export function Avatar() {
  return <Image src="/me.png" alt="me" width="64" height="64" />
}

缓存

Next.js 会自动为 public 文件夹中的不可变资源添加缓存标头。默认应用的缓存头为:

Cache-Control: public, max-age=31536000, immutable

Robots、Favicons 及其他

对于静态元数据文件,例如:robots.txtfavicon.ico 等,应在 app 文件夹内使用特殊的元数据文件。

需要知道:

  • ​ 目录必须命名为 public。该名称不能更改,而且它是唯一用于提供静态资源的目录。
  • ​ Next.js 只提供构建时位于 public 目录中的资源。请求时添加的文件将不可用。我们建议使用第三方服务(例如:AWS S3)进行持久文件存储。

包分析器

@next/bundle-analyzer 是 Next.js 的一个插件,可帮助你管理 JavaScript 模块的大小。它会生成一份可视化报告,说明每个模块的大小及其依赖关系。你可以利用这些信息移除大的依赖关系,拆分代码,或只在需要时加载某些部分,从而减少传输到客户端的数据量。

安装

运行以下命令安装插件:

npm i @next/bundle-analyzer
# or
yarn add @next/bundle-analyzer
# or
pnpm add @next/bundle-analyzer

然后,将包分析器的设置添加到 next.config.js 中。

// next.config.js

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})
 
/** @type {import('next').NextConfig} */
const nextConfig = {}
 
module.exports = withBundleAnalyzer(nextConfig)

分析你的包

运行以下命令分析包:

ANALYZE=true npm run build
# or
ANALYZE=true yarn build
# or
ANALYZE=true pnpm build

报告将在浏览器中打开三个新标签页,你可以检查它们。在开发过程中和部署网站前定期这样做,可以帮助你更早地识别大型包,并构建性能更高的应用程序。

懒加载

Next.js 中的懒加载通过减少渲染路由所需的 JavaScript 数量帮助提高应用程序的初始加载性能。

通过它,你可以延迟加载客户端组件和导入的库,只有在需要时才将它们包含在客户端包中。例如:你可能想推迟加载一个模态框,直到用户点击打开它。

有两种方法可以在 Next.js 中实现懒加载:

  • 使用 next/dynamic 进行动态导入
  • 使用带有 Suspense 的 React.lazy()

默认情况下,服务器组件会自动进行代码拆分,你可以使用流式传输将 UI 从服务器逐步发送到客户端。懒加载适用于客户端组件。

next/dynamic

next/dynamicReact.lazy() 和 Suspense 复合物。它在 apppages 目录中的行为方式相同,允许增量迁移。

示例

导入客户端组件
// app/page.js

'use client'
 
import { useState } from 'react'
import dynamic from 'next/dynamic'
 
// 客户端组件:
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
 
export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false)
 
  return (
    <div>
      {/* 立即家啊在, 但在一个单独的客户端包中 */}
      <ComponentA />
 
      {/* 按需加载,仅在满足条件时加载 */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Toggle</button>
 
      {/* 仅在客户端加载 */}
      <ComponentC />
    </div>
  )
}
跳过 SSR

使用 React.lazy() 和 Suspense 时,客户端组件默认会进行预渲染(SSR)。

如果要禁用客户端组件的预渲染,可以将 ssr 选项设为 false

const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
导入服务器组件

如果动态导入服务器组件,只有作为服务器组件子组件的客户端组件才会被懒加载,而不是服务器组件本身。

// app/page.js

import dynamic from 'next/dynamic'
 
// 服务器组件:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))
 
export default function ServerComponentExample() {
  return (
    <div>
      <ServerComponent />
    </div>
  )
}
加载外部库

可使用 import() 函数加载外部库。本示例使用外部库 fuse.js 进行模糊搜索。只有在用户输入搜索输入后,才会在客户端加载该模块。

// app/page.js

'use client'
 
import { useState } from 'react'
 
const names = ['Tim', 'Joe', 'Bel', 'Lee']
 
export default function Page() {
  const [results, setResults] = useState()
 
  return (
    <div>
      <input
        type="text"
        placeholder="Search"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // 动态加载 fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)
 
          setResults(fuse.search(value))
        }}
      />
      <pre>Results: {JSON.stringify(results, null, 2)}</pre>
    </div>
  )
}
添加自定义加载组件
// app/page.js

import dynamic from 'next/dynamic'
 
const WithCustomLoading = dynamic(
  () => import('../components/WithCustomLoading'),
  {
    loading: () => <p>Loading...</p>,
  }
)
 
export default function Page() {
  return (
    <div>
      {/* 加载组件将在 <WithCustomLoading/> 加载时渲染 */}
      <WithCustomLoading />
    </div>
  )
}
导入已命名的导出

要动态导入已命名的导出,可以从 import() 返回的 Promise 中返回该导出函数返回:

// components/hello.js

'use client'
 
export function Hello() {
  return <p>Hello!</p>
}
// app/page.js

import dynamic from 'next/dynamic'
 
const ClientComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)

分析

Next.js 内置支持性能指标的测量和报告。你可以使用 useReportWebVitals 钩子自行管理报告,或者使用 Vercel 提供的托管服务
为你自动收集和可视化指标。

独自构建

// app/_components/web-vitals.js

'use client'
 
import { useReportWebVitals } from 'next/web-vitals'
 
export function WebVitals() {
  useReportWebVitals((metric) => {
    console.log(metric)
  })
}
// app/layout.js

import { WebVitals } from './_components/web-vitals'
 
export default function Layout({ children }) {
  return (
    <html>
      <body>
        <WebVitals />
        {children}
      </body>
    </html>
  )
}

由于 useReportWebVitals 钩子需要 “use client” 指令,因此性能最高的方法是创建根布局导入的单独组件。这将客户端边界限制为 WebVitals 组件。

有关详细信息,请查看 API 参考。

网络指标

网络指标是一组有用的指标,旨在捕获网页的用户体验。以下网络指标都包括在内:

  • Time to First Byte(TTFB)
  • First Contentful Paint(FCP)
  • Largest Contentful Paint(LCP)
  • First Input Delay(FID)
  • Cumulative Layout Shift(CLS)
  • Interaction to Next Paint(INP)

你可以使用 name 属性处理这些指标的所有结果。

// app/components/web-vitals.tsx

'use client'
 
import { useReportWebVitals } from 'next/web-vitals'
 
export function WebVitals() {
  useReportWebVitals((metric) => {
    switch (metric.name) {
      case 'FCP': {
        // 处理 FCP 结果
      }
      case 'LCP': {
        // 处理 LCP 结果
      }
      // ...
    }
  })
}

将结果发送到外部系统

你可以将结果发送到任何端点,以衡量和跟踪网站上的真实用户性能。例如:

useReportWebVitals((metric) => {
  const body = JSON.stringify(metric)
  const url = 'https://example.com/analytics'
 
  // 如果可以的话,使用 `navigator.sendBeacon()`,回退到 `fetch()`
  if (navigator.sendBeacon) {
    navigator.sendBeacon(url, body)
  } else {
    fetch(url, { body, method: 'POST', keepalive: true })
  }
})

需要知道:如果你使用 Google Analytics(分析),使用 id 值可以手动构建指标分布(计算百分位数等)

useReportWebVitals(metric => {
  // 如果你初始化了 Google Analytics,请使用 `window.gtag`,如下所示:
  // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_app.js
  window.gtag('event', metric.name, {
    value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), // 值必须为整数
    event_label: metric.id, // 当前页面加载时唯一的 id
    non_interaction: true, // 避免影响跳出率
  });
}

详细了解如何将结果发送到 Google Analytics。

仪表

仪表是使用代码将监控和日志工具集成到应用程序中的过程。这样,你就可以跟踪应用程序的性能和行为,并调试生产中的问题。

Next.js 提供了一个 register 函数,该函数可从项目根目录(如果使用 src 文件夹,则位于 src 文件夹内)中的 instrumentation.ts|js 文件导出。每当启动一个新的 Next.js 服务器实例时,Next.js 就会调用 register 函数。

需要知道

  • 此功能是试验性的。要使用该功能,必须在 next.config.js 中定义 experimental.instrumentationHook = true; 以明确选择加入。
  • instrrumentation 文件应放在项目根目录下,而不是 apppages 目录下。如果使用的是 src 文件夹,则应将该文件放在 src 目录中,与 pagesapp 放在一起。
  • 如果使用 pageExtensions 配置选项添加后缀,还需要更新 instrumentation 文件名以匹配。
  • 我们创建了一个基本的 with-opentelemetry 的示例。

部署 register 函数后,每次冷启动时都会调用该函数(但在每个环境中都会调用一次)。

有时,在代码中导入文件可能会产生一些副作用。例如:你可能会导入一个定义了全局变量集的文件,但绝不会在代码中明确使用导入的文件。你仍然可以访问包中声明的全局变量。

你可以在 instrumentation.ts 中导入具有副作用的文件,你可能希望在 register 函数中使用这些文件,如下例所示:

// your-project/instrumentation.ts

import { init } from 'package-init'
 
export function register() {
  init()
}

不过,我们建议使用 register 函数中的 import 来导入具有副作用的文件。下面的示例演示了在 register 函数中 import 的基本用法:

// your-project/instrumentation.ts

export async function register() {
  await import('package-with-side-effect')
}

这样做可以将所有副作用集中在代码中的一个地方,避免导入文件造成任何意想不到的后果。

我们会在所有环境中调用 register,因此有必要有条件地导入任何不支持 edgenodejs 的代码。你可以使用环境变量 NEXT_RUNTIME 来获取当前环境。导入特定环境的代码如下所示:

// your-project/instrumentation.ts

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation-node')
  }
 
  if (process.env.NEXT_RUNTIME === 'edge') {
    await import('./instrumentation-edge')
  }
}

OpenTelemetry

需要知道: 此功能是试验性的,你需要在 next.config.js 中提供 experimental.instrumentationHook = true; 以明确选择加入。

可观察性对于了解和优化 Next.js 应用程序的行为和性能至关重要。

随着应用程序变得越来越复杂,识别和诊断可能出现的问题也变得越来越困难。通过利用日志和指标等可观察性工具,开发人员可以深入了解应用程序的行为,并确定需要优化的领域。有了可观察性,开发人员就能在问题演变成重大问题之前主动加以解决,并提供更好的用户体验。因此,强烈建议在 Next.js 应用程序中使用可观察性来提高性能、优化资源和增强用户体验。

我们建议使用 OpenTelemetry 来检测应用程序。这是一种与平台无关的应用程序检测方式,允许你在不更改代码的情况下更改可观察性提供者。阅读 OpenTelemetry 官方文档了解有关 OpenTelemetry 及其工作原理的更多信息。

本文档中使用了 Span、Trace 或 Exporter 等术语,所有这些术语都可以在 OpenTelemetry Observability Primer 中找到。

Next.js 支持开箱即用的 OpenTelemetry 仪表,这意味着我们已经为 Next.js 本身安装了仪器。启用 OpenTelemetry 后,我们会自动将所有代码(例如:getStaticProps)封装在带有有用属性的 spans 中。

需要知道:我们目前仅在无服务器功能中支持 OpenTelemetry 绑定。我们不为 edge 或客户端代码提供任何绑定。

开始使用

OpenTelemetry 具有可扩展性,但正确设置它可能相当繁琐。因此,我们准备了一个 @vercel/otel 软件包,帮助你快速入门。它不具有可扩展性,如果你需要自定义设置,则应手动配置 OpenTelemetry。

使用 @vercel/otel

使用之前,需要安装 @vercel/otel

npm install @vercel/otel

接下来,在项目根目录(如果使用 src 文件夹,则在 src 文件夹内)创建自定义 instrumentation.ts 文件(或 .js 文件):

// your-project/instrumentation.ts

import { registerOTel } from '@vercel/otel'
 
export function register() {
  registerOTel('next-app')
}

需要知道

  • instrumentation 文件应放在项目根目录下,而不是 apppages 目录下。如果使用的是 src 文件夹,则应将文件放在 src 目录中,与 pagesapp 放在一起。
  • 如果使用 pageExtensions 配置选项添加后缀,还需要更新 instrumentation 文件名以匹配。
  • 我们创建了一个基本的 with-opentelemetry 的示例。
手动配置 OpenTelemetry

如果我们的封装程序 @vercel/otel 不能满足你的需求,你可以手动配置 OpenTelemetry。

首先需要安装 OpenTelemetry 软件包:

npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http

现在,你可以在 instrumentation.ts 中初始化 NodeSDK。OpenTelemetry API 与 edge 运行时不兼容,因此需要确保只有在 process.env.NEXT_RUNTIME === 'nodejs' 时才导入它们。我们建议创建一个新文件 instrumentation.node.ts,只有在使用 node 时才有条件导入:

// instrumentation.ts

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.ts')
  }
}
// instrumentation.node.ts

import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
 
const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()

这样做等同于使用 @vercel/otel,但可以修改和扩展。例如,可以使用 @opentelemetry/exporter-trace-otlp-grpc 代替 @opentelemetry/exporter-trace-otlp-http 或指定更多资源属性。

测试仪表

你需要一个兼容后端的 OpenTelemetry 收集器,以便在本地测试 OpenTelemetry 跟踪。我们建议使用我们的 OpenTelemetry 开发环境。

如果一切运行正常,你应该可以看到标为 GET /requested/pathname 的根服务器跨度。该特定跟踪的所有其他跨度都将嵌套在该跨度之下。

Next.js 会跟踪比默认情况下更多的跨度。要查看更多跨度,必须设置 NEXT_OTEL_VERBOSE=1

部署

使用 OpenTelemetry 收集器

使用 OpenTelemetry 收集器部署时,可以使用 @vercel/otel。它既可以在 Vercel 上部署,也可以自行部署。

在 Vercel 上部署

我们确保 OpenTelemetry 在 Vercel 上开箱即用。

按照 Vercel 文档将你的项目连接到可观测性提供者。

自托管

部署到其他平台也很简单。你需要启动自己的 OpenTelemetry 收集器来接收和处理来自 Next.js 应用程序的遥测数据。

为此,请遵循 OpenTelemetry Collector 入门指南,它将指导你设置收集器并配置它以接收来自 Next.js 应用程序的数据。

安装并运行收集器后,就可以按照各自的部署指南将 Next.js 应用程序部署到所选平台。

自定义输出程序

我们建议使用 OpenTelemetry 收集器。如果在你的平台上无法实现,你可以使用自定义 OpenTelemetry 输出程序,并进行手动 OpenTelemetry 配置。

自定义跨度

你可以使用 OpenTelemetry APIs 添加自定义跨度。

npm install @opentelemetry/api

下面的示例演示了一个获取 GitHub stars 并添加自定义 fetchGithubStars 维度以跟踪获取请求结果的函数:

import { trace } from '@opentelemetry/api'
 
export async function fetchGithubStars() {
  return await trace
    .getTracer('nextjs-example')
    .startActiveSpan('fetchGithubStars', async (span) => {
      try {
        return await getValue()
      } finally {
        span.end()
      }
    })
}

register 函数将在代码在新环境中运行前执行。你可以开始创建新的跨度,它们应该会被正确地添加到导出的跟踪中。

Next.js 中的默认跨度

Next.js 会自动为你设置多个跨度,以便为你提供有关应用程序性能的有用信息。

跨度属性遵循 OpenTelemetry 语义约定。我们还在 next 命名空间下添加了一些自定义属性:

  • next.span_name - 重复跨度名称
  • next.span_type - 每种跨度类型都有一个唯一标识符
  • next.route - 请求的路由模式(例如:/[param]/user
  • next.page
    • 这是应用程序路由器使用的内部值
    • 你可以将其视为通往特殊文件(例如:page.tslayout.tsloading.ts 等)的路由
    • 只有与 next.route 搭配使用时,它才能用作唯一标识符,因为 /layout 可同时用于标识 /(groupA)/layout.ts/(groupB)/layout.ts
[http.method] [next.route]
  • next.span_type: BaseServer.handleRequest

此跨度表示传入 Next.js 应用程序的每个请求的根跨度。它跟踪 HTTP 方法、路由、目标和请求的状态代码。

属性:

  • 通用 HTTP 属性

    • http.method

    • http.status_code

  • 服务器 HTTP 属性

    • http.route

    • http.target

  • next.span_name

  • next.span_type

  • next.route

render route (app) [next.route]
  • next.span_type: AppRender.getBodyResult

此跨度表示在应用程序路由器中渲染路由的过程。

属性:

  • next.span_name
  • next.span_type
  • next.route
fetch [http.method] [http.url]
  • next.span_type: AppRender.fetch

该跨度表示代码中执行的获取请求。

属性:

  • 通用 HTTP 属性

    • http.method
  • 客户端 HTTP 属性

    • http.url

    • net.peer.name

    • net.peer.port(仅当指定)

  • next.span_name

  • next.span_type

executing api route (app) [next.route]
  • next.span_type: AppRouteRouteHandlers.runHandler

此跨度表示在应用程序路由器中执行 API 路由处理程序。

属性:

  • next.span_name
  • next.span_type
  • next.route
getServerSideProps [next.route]
  • next.span_type: Render.getServerSideProps

此跨度表示特定路由的 getServerSideProps 执行情况。

属性:

  • next.span_name
  • next.span_type
  • next.route
getStaticProps [next.route]
  • next.span_type: Render.getStaticProps

此跨度表示特定路由的 getStaticProps 执行情况。

属性:

  • next.span_name
  • next.span_type
  • next.route
render route (pages) [next.route]
  • next.span_type: Render.renderDocument

该跨度表示为特定路由渲染文档的过程。

属性:

  • next.span_name
  • next.span_type
  • next.route
generateMetadata [next.page]
  • next.span_type: ResolveMetadata.generateMetadata

该跨度表示为特定页面生成元数据的过程(一个路由可以有多个这样的跨度)。

属性:

  • next.span_name
  • next.span_type
  • next.page

第三方库

@next/third-parties 是一个库,它提供了一系列组件和实用程序,可改善在 Next.js 应用程序中加载常用第三方库的性能和开发人员体验。

@next/third-parties 提供的所有第三方集成都经过了性能和易用性方面的优化。

开始使用

要开始使用,请安装 @next/third-parties 库:

npm install @next/third-parties@latest next@latest

@next/third-parties 目前是一个正在积极开发的实验性库。在我们努力添加更多第三方集成时,建议使用最新金丝雀标志安装。

谷歌第三方

可从 @next/third-parties/google 中导入谷歌支持的所有第三方库。

谷歌标签管理器

GoogleTagManager 组件可用于在页面中实例化一个 Google 标签管理器容器实例化到你的页面。默认情况下,它会在页面水合后获取原始内嵌脚本。

要为所有路径加载 Google 标签管理器,请直接在根布局中包含该组件,并输入 GTM 容器 ID:

// app/layout.tsx

import { GoogleTagManager } from '@next/third-parties/google'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleTagManager gtmId="GTM-XYZ" />
    </html>
  )
}

要为单个路由加载 Google 标签管理器,请在页面文件中包含该组件:

// app/page.js

import { GoogleTagManager } from '@next/third-parties/google'
 
export default function Page() {
  return <GoogleTagManager gtmId="GTM-XYZ" />
}
发送事件

sendGTMEvent 函数可通过使用 dataLayer 对象发送事件来跟踪页面上的用户交互。要使用该函数,<GoogleTagManager /> 组件必须包含在父布局、页面或组件中,或直接包含在同一文件中。

// app/page.js

'use client'
 
import { sendGTMEvent } from '@next/third-parties/google'
 
export function EventButton() {
  return (
    <div>
      <button
        onClick={() => sendGTMEvent({ event: 'buttonClicked', value: 'xyz' })}
      >
        Send Event
      </button>
    </div>
  )
}

请参阅标签管理器开发人员文档了解可传入函数的不同变量和事件。

选项

传递给 Google 标签管理器的选项。有关选项的完整列表,请阅读 Google 标签管理器文档。

名称类型描述
gtmId必须你的 GTM 容器 ID。通常以 GTM- 开头。
dataLayer可选用于实例化容器的数据层数组。默认为空数组。
dataLayerName可选数据层的名称。默认为 dataLayer
auth可选环境片段的验证参数 (gtm_auth) 值。
preview可选环境片段的预览参数 (gtm_preview) 值。
谷歌分析

GoogleAnalytics 组件可用于将 Google Analytics 4 通过 Google 标签(gtag.js)添加到你的页面。默认情况下,它会在页面水合后获取原始脚本。

建议使用: 如果你的应用程序中已包含 Google 标签管理器,你可以直接使用它来配置 Google 分析,而无需将 Google 分析作为单独的组件包含在内。请参阅文档了解标签管理器和 gtag.js 之间的区别。

要为所有路由加载 Google 分析,请在根布局中直接包含该组件,并输入测量 ID:

// app/layout.tsx

import { GoogleAnalytics } from '@next/third-parties/google'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleAnalytics gaId="G-XYZ" />
    </html>
  )
}

要为单个路由加载 Google 分析,请在页面文件中包含该组件:

// app/page.js

import { GoogleAnalytics } from '@next/third-parties/google'
 
export default function Page() {
  return <GoogleAnalytics gaId="G-XYZ" />
}
发送事件

sendGAEvent 函数可通过使用 dataLayer 对象发送事件来测量用户在页面上的交互。要使用该函数,<GoogleAnalytics /> 组件必须包含在父布局、页面或组件中,或直接包含在同一文件中。

请参阅 Google 分析开发人员文档了解有关事件参数的更多信息。

跟踪页面浏览

当浏览器历史状态发生变化时,Google 分析会自动跟踪页面浏览。这意味着在 Next.js 路由之间进行客户端导航时,无需任何配置即可发送页面浏览数据。

要确保客户端导航被正确测量,请在管理面板中确认 “Enhanced Measurement” 属性已启用,并选中 “基于浏览器历史事件的页面更改” 复选框。

注意:如果决定手动发送页面浏览事件,请确保禁用默认的页面浏览测量,以避免数据重复。请参阅 Google 分析开发人员文档了解更多信息。

选项

要传递给 <GoogleAnalytics> 组件的选项。

名称类型描述
gaId必须你的测量 ID。通常以 G- 开头。
dataLayerName可选数据层的命名。默认为 dataLayer
谷歌地图嵌入

GoogleMapsEmbed 组件可用于在你的页面上添加一个 Google 地图嵌入到你的页面。默认情况下,它会使用 loading 属性将嵌入内容懒散地加载到折叠下方。

// app/page.js

import { GoogleMapsEmbed } from '@next/third-parties/google'
 
export default function Page() {
  return (
    <GoogleMapsEmbed
      apiKey="XYZ"
      height={200}
      width="100%"
      mode="place"
      q="Brooklyn+Bridge,New+York,NY"
    />
  )
}
选项

传递给 Google 地图嵌入程序的选项。有关选项的完整列表,请阅读 Google 地图嵌入文档。

名称类型描述
apiKey必须你的 api key。
mode必须地图模式
height可选嵌入的高度。默认是 auto
width可选嵌入的宽度。默认是 auto
style可选将样式传递给 iframe。
allowfullscreen可选允许某些地图部分全屏显示的属性。
loading可选默认为懒加载。如果你知道你的嵌入将位于折叠上方,请考虑更改。
q可选定义地图标记位置。根据地图模式的不同,这可能是必需的。
center可选定义地图视图的中心。
zoom可选设置地图的初始缩放级别。
maptype可选定义要加载的地图类型。
language可选定义 UI 元素和地图标签显示所使用的语言。
region可选根据地缘政治敏感性,定义要显示的适当边界和标签。
YouTube 嵌入

YouTubeEmbed 组件可用于加载和显示 YouTube 嵌入。通过在引擎盖下使用 lite-youtube-embed 来加快加载速度。

// app/page.js

import { YouTubeEmbed } from '@next/third-parties/google'
 
export default function Page() {
  return <YouTubeEmbed videoid="ogfYd705cRs" height={400} params="controls=0" />
}
选项
命名类型描述
videoid必须YouTube 视频 id。
width可选视频容器的宽度。默认为 auto
height可选视频容器的高度。默认为 auto
playlabel可选为播放按钮添加可视化隐藏标签,以方便使用。
params可选视频播放器默认的参数。参数以查询参数字符串的形式传递。
例如:params="controls=0&start=10&end=30"
style可选用于为视频容器应用样式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1418828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink问题解决及性能调优-【Flink rocksDB读写state大对象导致背压问题调优】

RocksDB是Flink中用于持久化状态的默认后端&#xff0c;它提供了高性能和可靠的状态存储。然而&#xff0c;当处理大型状态并频繁读写时&#xff0c;可能会导致背压问题&#xff0c;因为RocksDB需要从磁盘读取和写入数据&#xff0c;而这可能成为瓶颈。 遇到的问题 Flink开发…

STM32 有源蜂鸣器

模块介绍: 结构&#xff1a;有源蜂鸣器通常由一个振膜和一个驱动电路组成。振膜是负责产生声音的部分&#xff0c;而驱动电路则负责控制振荡频率和幅度。 工作原理&#xff1a;有源蜂鸣器的驱动电路会向振膜施加电压&#xff0c;使其振动产生声音。驱动电路可以根据输入信号的…

centos7安装mysql5.7 或者mysql8

1、centos7安装mysql8 mysql官网 https://dev.mysql.com/downloads/mysql/ 示例2个版本的下载地址 #5.7.30下载地址 wget https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.30-1.el7.x86_64.rpm-bundle.tar #8.0.22下载地址 wget https://cdn.mysql.com/archives/mysql-8…

网络防御保护——课程笔记

一.防火墙 防火墙的主要职责&#xff1a;控制和防护 --- 安全策略 --- 防火墙可以根据安全策略来抓取流量之后做出对应的动作。 防火墙的分类 防火墙的发展进程 防火墙的控制 带内管理 --- 通过网络环境对设备进行控制 --- telnet&#xff0c;ssh&#xff0c;web --- 登录设备…

【node】Node.js的常用内置模块:

文章目录 一、os模块&#xff1a;【1】常用的OS模块方法包括&#xff1a;【2】案例&#xff1a; 二、path模块&#xff1a;【1】常用的path模块方法包括&#xff1a;【2】案例&#xff1a; 三、url模块&#xff1a;【1】常用的url模块方法包括&#xff1a;【2】案例&#xff1a…

【Apache POI】百万级数据导出Excel,并含有折线等图表

需求概要 最近接到一个需求&#xff0c;概要来讲就是实现百万级数据导出Excel&#xff0c;并根据其中的数据项自动生成折线图等图表。经技术调研&#xff0c;针对内存、性能等要素&#xff0c;Apache POI此技术可完成此需求。 Apache POI是Apache软件基金会的开放源码函式库&am…

《教我兄弟学Android逆向15 xpose改机开发03-写一款自己的改机软件》

上一篇 《教我兄弟学Android逆向14 xpose改机开发02-改机代码基础编写》我带你熟悉了一下改机的流程,搭建了改机的基础代码,改机参数数据储存方面我们用的是SharedPreferences存储数据,界面方面写了一个一键新机的按钮,点一键新机的时候会随机生成imei数据存储到xml数据中,然后…

isctf---re

crackme 解压得到crackme.exe 运行得到flag babyRe 先猜e65537的rsa 先用Z3强行求出p、q&#xff0c;算出常规rsa中的phi&#xff0c;然后套用公式求出m exp #babyre wp from z3 import * import libnum from Crypto.Util.number import * p,q,cInts(p q c) S Solver() S…

【前端web入门第一天】02 HTML图片标签 超链接标签 音频标签 视频标签

文章目录: 1.HTML图片标签 1.1 图像标签-基本使用1.2 图像标签-属性1.3 路径 1.3.1 相对路径 1.3.2 绝对路径 2.超链接标签 3.音频标签 4.视频标签 1.HTML图片标签 1.1 图像标签-基本使用 作用:在网页中插入图片。 <img src"图片的URL">src用于指定图像…

前端框架---Vue2学习教程(上)

从HTML到现在一路跟过来的小伙伴们&#xff0c;坚持固然不容易&#xff0c;但我相信大家已经学到了不少&#xff0c;那么我们开始马不停蹄的进入前端的框架吧&#xff0c;下面讲的是Vue2&#xff0c;大家继续加油鸭&#xff01;&#xff01;&#xff01;&#xff01; Vue2 Vu…

遗传算法优化最大化效应的某些需求点可不配送的vrptw问题

标题&#xff1a;遗传算法优化最大化效应的某些需求点可不配送的vrptw问题 摘要&#xff1a; 在可不配送的车辆路径配送问题&#xff08;VRPTW&#xff09;中&#xff0c;我们面临着优化路径规划以最大化效用的挑战。本文提出了一种基于遗传算法的方法&#xff0c;旨在解决具…

【Shell实战案例面试题】输入网卡的名字,来输出网卡的IP

1.问题 参数后判断要加"" 名字为空时显示ip 2.分析 把本机的所有网卡名列出来&#xff0c;来引导用户输入 使用命令列出所有网卡信:ifconfig/ip a 设计一个函数&#xff0c;把网卡名作为参数&#xff0c;函数返回网卡的IP 在获取某个网卡IP时&#xff0c;考虑网…

Redis面试(三)

1.Redis报内存不足怎么处理 Redis内存不足的集中处理方式&#xff1a; 修改配置文件redis.cof的maxmemory参数&#xff0c;增加Redis的可用内存通过命令修改set maxmemory动态设置内存上限修改内存淘汰策略&#xff0c;及时释放内存使用Redis集群&#xff0c;及时进行扩容 2…

基于springboot的美发管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

让Unity迭代器性能提升5倍

最近在研究Unity il2cpp的代码生成和编译优化&#xff0c;结合之前遇到过的一个优化案例&#xff0c;给大家讲讲在Unity中迭代器相关代码生成的底层原理&#xff0c;以及在写代码过程中需要注意的一些特殊情况。 案例 首先我们来看一个非常简单的案例&#xff0c;代码如下&am…

3338 蓝桥杯 wyz的数组IV 简单

3338 蓝桥杯 wyz的数组IV 简单 //C风格解法1&#xff0c;通过率50% #include<bits/stdc.h>int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);int n; std::cin >> n;int ans 0;std::vector<int>a(n);for(auto &am…

Docker本地部署APITable结合内网穿透实现公网访问

文章目录 前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c;是一款面向 API 的智能多维表格。它将复杂的可视化数据库、电子表格、实时在线协同、低代码开发技术四合为一&am…

【C语言】socket编程接收问题

一、recv()函数接收到的返回值为0表示对端已经关闭 在TCP套接字编程中&#xff0c;通过recv()函数接收到的返回值为0通常表示对端已经关闭了套接字的发送部分。这是因为TCP是一个基于连接的协议&#xff0c;其中有定义明确的连接建立和终止流程&#xff1b;当对端调用close()或…

C语言指针进阶(1)(超详细)

前言&#xff1a; 指针其实就是地址&#xff0c;而凡是存储在内存中的值都会有属于自己的地址&#xff0c;指针指向地址&#xff0c;这样我们就能通过指针间接操作变量。我们在指针初阶中介绍了指针的基本概念&#xff1a;如指针大小、野指针问题、指针间的关系运算等&#xff…

Hugging Face创始人分享:企业如何在ChatGPT浪潮下实现战略布局

Hugging Face创始人兼首席执行官 Clem Delangue在IBM一年一度的 THINK大会中研讨了当前人工智能发展趋势&#xff0c;特别是ChatGPT模型以及其对行业的影响。他的演讲还涉及到一个关键的议题&#xff0c;在ChatGPT这样的通用模型出现后&#xff0c;企业如何在人工智能领域找到自…