Next.js 提供了多种内置优化,旨在提高应用程序的速度和核心网络生命指数。本指南将介绍可用于增强用户体验的优化功能。
内置组件抽象化了实现常见 UI 优化的复杂性。这些组件包括:
元素构建。图像组件通过延迟加载和根据设备大小自动调整图像大小来优化图像的性能。
标签构建。链接组件在后台预取页面,以实现更快、更流畅的页面过渡。
标签构建。脚本组件使你可以控制第三方脚本的加载和执行。元数据可帮助搜索引擎更好地理解你的内容(从而提高搜索引擎优化效果),并允许你自定义内容在社交媒体上的展示方式,帮助你在不同平台上创建更具吸引力和一致性的用户体验。
通过 Next.js 中的元数据 API,你可以修改页面的 元素。你可以通过两种方式配置元数据:
layout.js
或 page.js
文件中导出静态 metadata
对象或动态 generateMetadata
函数。此外,你还可以通过 imageResponse 构造函数使用 JSX 和 CSS 创建动态 Open Graph 图片。
Next.js /public
文件夹可用于提供静态资源,例如:图片、字体和其他文件。/public
文件夹中的文件还可由 CDN 提供商缓存,以便高效分发。
对于大型应用程序,Next.js 集成了流行的分析和监控工具,可帮助你了解应用程序的运行情况。了解更多信息,请参阅 OpenTelemetry 和 Instrumentation 指南。
示例:
根据 Web Almanac,图像占典型网站页面权重的很大一部分,并且会对你网站的 LCP 性能产生相当大的影响。
Next.js 图像组件使用自动图像优化功能扩展了 HTML 元素:
**观看:**了解更多有关如何使用
next/image
→ YouTube (9 minutes)。
import Image from 'next/image'
然后,你就可以定义图片的 src
(本地或远程)。
要使用本地图像,请 import
你的 .jpg
、.png
或 .webp
图像文件。
Next.js 将根据导入的文件自动确定图片的 width
和 height
。这些值用于防止在加载图片时发生累积布局偏移。
// 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 在构建过程中无法访问远程文件,因此需要手动提供 width
、height
和可选的 blurDataURL
props。
width
和 height
属性用于推断图像的正确纵横比,避免加载图像时出现布局偏移。width
和 height
并不决定图像文件的渲染大小。了解有关图像大小的更多信息。
// 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
属性的
,就会在控制台显示警告。
一旦确定了 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
的设计是为了保证良好的性能效果,因此在使用时不能造成布局偏移,必须以以下三种方式之一确定大小:
width
和 height
属性如果我不知道图像的尺寸怎么办?
如果你在不知道图片大小的情况下从源访问图片,你可以采取以下几种方法:
使用
fill
fill
prop 允许图像按其父元素调整大小。考虑使用 CSS 在页面上沿sizes
prop 为图像的父元素提供空间,以匹配任何媒体查询断点。你还可以使用带有fill
、contain
或cover
的object-fit
,以及object-position
来定义图像应如何占据该空间。对图像进行规范化
如果要从你控制的源提供图像,请考虑修改图像管道,以将图像规范化为特定大小。
修改 API 调用
如果你的应用程序使用 API 调用(例如:对 CMS)检索图像 URL,则可以修改 API 调用以返回图像尺寸和 URL。
如果建议的方法都无法调整图片大小,next/image
组件可以在页面上与标准 元素一起使用。
图像组件的样式与普通 元素的样式类似,但有几条准则需要注意:
使用 className
或 style
,而不是 styled-jsx
className
prop。这可以是导入的 CSS 模块、全局样式表等style
prop 来分配内联样式styled-jsx
,因为它的作用域是当前组件(除非你将样式标记为 global
)使用 fill
时,父元素必须设置 position: relative
使用 fill
时,父元素必须设置 display: block
元素的默认设置,但应另行指定
示例
响应式
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 字体会自动子集。这样可以减小字体文件的大小并提高性能。你需要定义要预载的子集。如果在 preload
为 true
时未指定任何子集,将导致警告。
这可以通过在函数调用中添加来实现:
// 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
将被全局应用,任何
标签都将使用 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-sans
和 font-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 dev
或 yarn 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 参考资料,了解每个事件处理程序的更多信息并查看示例。
附加属性
可以为
元素分配许多脚本组件未使用的 DOM 属性,例如:nonce
或自定义数据属性。包含任何附加属性都会自动转发到 HTML 中最终优化的
元素。
// 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
元素中的 meta
和 link
标签),以提高搜索引擎优化和网络共享性。
有两种方法可以为应用程序添加元数据:
- 基于配置的元数据: 在
layout.js
或 page.js
文件中导出静态 metadata
对象或动态 generateMetadata
函数。
- 基于文件的元数据: 为路由段添加静态或动态生成的特殊文件。
有了这两个选项,Next.js 会自动为页面生成相关的
元素。你还可以使用 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
生成的静态和动态元数据仅在服务器组件中受支持。
- 对于
generateMetadata
、generateStaticParams
、布局、页面和服务器组件中的相同数据,会自动对 fetch
请求进行记忆。如果无法 fetch
,可以使用 React cache
。
- Next.js 将等待
generateMetadata
中的数据获取完成后,再将用户界面流式传输到客户端。这将确保流式响应的第一部分包含
标记。
基于文件的元数据
这些特殊文件可用于元数据:
- 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
(博客页面)
合并
按照评估顺序,从同一路由的多个片段中导出的元数据对象会浅层合并在一起,形成路由的最终元数据输出。重复的键会根据其排序进行替换。
这意味着带有嵌套字段(例如:openGraph
和 robots
)的元数据,如果在较早的分段中定义,则会被最后一个定义它们的分段覆盖。
覆盖字段
// 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',
},
}
// 输出:
// 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.js
和 app/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',
}
// 输出:
// About
//
//
注释
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、字体、图像和任何其他资源。如果超出限制,请考虑减小任何资源的大小或在运行时获取。
- 仅支持
ttf
、otf
和 woff
字体格式。为了最大限度地提高字体解析速度,ttf
或 otf
比 woff
更受青睐。
JSON-LD
JSON-LD 是一种结构化数据格式,搜索引擎可以用它来理解你的内容。例如:你可以用它来描述一个人、一个事件、一个组织、一部电影、一本书、一份食谱以及许多其他类型的实体。
对于 JSON-LD,我们目前的建议是在 layout.js
或 page.js
组件中以
标签的形式渲染结构化数据。例如:
// 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.txt
、favicon.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/dynamic
是 React.lazy()
和 Suspense 复合物。它在 app
和 pages
目录中的行为方式相同,允许增量迁移。
示例
导入客户端组件
// 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 />
</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
文件应放在项目根目录下,而不是 app
或 pages
目录下。如果使用的是 src
文件夹,则应将该文件放在 src
目录中,与 pages
和 app
放在一起。
- 如果使用
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
,因此有必要有条件地导入任何不支持 edge
和 nodejs
的代码。你可以使用环境变量 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
文件应放在项目根目录下,而不是 app
或 pages
目录下。如果使用的是 src
文件夹,则应将文件放在 src
目录中,与 pages
和 app
放在一起。
- 如果使用
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.ts
、layout.ts
、loading.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
对象发送事件来跟踪页面上的用户交互。要使用该函数,
组件必须包含在父布局、页面或组件中,或直接包含在同一文件中。
// 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
对象发送事件来测量用户在页面上的交互。要使用该函数,
组件必须包含在父布局、页面或组件中,或直接包含在同一文件中。
请参阅 Google 分析开发人员文档了解有关事件参数的更多信息。
跟踪页面浏览
当浏览器历史状态发生变化时,Google 分析会自动跟踪页面浏览。这意味着在 Next.js 路由之间进行客户端导航时,无需任何配置即可发送页面浏览数据。
要确保客户端导航被正确测量,请在管理面板中确认 “Enhanced Measurement” 属性已启用,并选中 “基于浏览器历史事件的页面更改” 复选框。
注意:如果决定手动发送页面浏览事件,请确保禁用默认的页面浏览测量,以避免数据重复。请参阅 Google 分析开发人员文档了解更多信息。
选项
要传递给
组件的选项。
名称
类型
描述
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
可选
用于为视频容器应用样式。