Next 元数据(metadata)

基于配置的元数据

静态元数据还是动态元数据都只在服务端组件中支持,尽可能使用静态元数据。

静态元数据

在 layout.js 或 page.js 中,导出 Metadata 对象

1
2
3
4
export const metadata = {
title: 'Next.js',
description: 'Next.js is a framework for building dynamic websites',
}

动态元数据

使用动态元数据要借助 generateMetadata 函数返回一个 Metadata 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export async function generateMetadata({ params, searchParams }, parent) {
// 读取路由参数
const id = params.id

// 获取数据
const product = await fetch(`https://.../${id}`).then((res) => res.json())

// 获取和拓展父路由段 metadata
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 和 parent,props 包含当前路由参数的对象,该对象有 params 和 searchParams 两个属性

params 是动态路由参数的对象,当访问非动态路由,params 为 {}

路由 url params
app/shop/[slug]/page.js /shop/1 { slug: ‘1’ }
app/shop/[tag]/[item]/page.js /shop/1/2 { tag: ‘1’, item: ‘2’ }
app/shop/[…slug]/page.js /shop/1/2 { slug: [‘1’, ‘2’] }

searchParams 是 URLSearchParams 对象,包含了 URL 中的查询参数

url searchParams
/shop?a=1 { a: ‘1’ }
/shop?a=1&b=2 { a: ‘1’, b: ‘2’ }
/shop?a=1&a=2 { a: [‘1’, ‘2’] }

parent 是一个包含父路由段 metadata 对象的 promise 对象,需要使用 (await parent).openGraph 获取。

字段覆盖

Next 会对各层的元数据进行浅合并,离当前页面越近的元数据会覆盖离当前页面越远的元数据。

字段继承

当前页的元数据会继承自己没定义的上层的元数据。

1
2
3
4
5
6
7
8
// app/layout.js
export const metadata = {
title: 'layout',
openGraph: {
title: '...',
description: '...',
},
}
1
2
3
4
// app/about/page.js
export const metadata = {
title: 'page',
}

如上,page.js 中未定义 openGraph 而 他的上层 layout 定义了,所以 page 会继承 layout 的 openGraph 字段。

JSON-LD

JSON-LD 可以向搜索引擎描述网站上的内容。 在 Next 中使用 JSON-LD 可以在 layout 或者 page 中使用 script 标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* ... */}
</section>
)
}

基于文件的元数据

基于文件的元数据优先级更高,会覆盖基于配置的元数据。

图标

Next 的图标就是在 /app 目录下的 faviconiconapple-icon 图片文件。

favicon 必须要在顶层目录(如 /app 或 /src/app)下,iconapple-icon 可以放在更深层的目录。

File convention Supported file types Valid locations Link ref
favicon .ico app/
icon .ico、.jpg、.jpeg、.png、.svg app/**/*
apple-icon .jpg、.jpeg、.png app/**/*

不同尺寸图标

还可以在一个目录下放多个不同尺寸 icon ,可以根据不同尺寸显示不同 icon。

使用代码生成

文件名还是 icon 或者 apple-icon 后缀为 js、ts、tsx,通过 next/og的 ImageResponse 即可生成一个图标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// app/icon.js
import { ImageResponse } from 'next/og'

// 路由段配置
export const runtime = 'edge'

// 图片 metadata
export const size = {
width: 32,
height: 32,
}
export const contentType = 'image/png'

// 图片生成
export default function Icon() {
return new ImageResponse(
(
<div
style={{
fontSize: 24,
background: 'black',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
}}
>
A
</div>
),
// ImageResponse options
{
// 方便复用 size
...size,
}
)
}

robot

robot 用于告诉搜索引擎可以爬取网站中的哪些 URL,Next 中有两种方式设置 robot。

  1. 静态文件
    在 app/ 下创建 robot.txt 即可
    1
    2
    3
    4
    User-Agent: *
    Allow: /
    Disallow: /private/
    Sitemap: https://acme.com/sitemap.xml
  2. 代码生成
    使用 robot.js / robot.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    export default function robots() {
    return {
    rules: {
    userAgent: '*',
    allow: '/',
    disallow: '/private/',
    },
    sitemap: 'https://acme.com/sitemap.xml',
    }
    }

站点地图 sitemap.xml

站点地图是一个 XML 文件,用于告诉搜索引擎网站的结构,Next 中可以使用静态文件 sitemap.xml 或者 sitemap.js / sitemap.ts 生成站点地图。

  1. 静态文件
    app/sitemap.xml:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
    <loc>https://acme.com</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>yearly</changefreq>
    <priority>1</priority>
    </url>
    <url>
    <loc>https://acme.com/about</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
    </url>
    <url>
    <loc>https://acme.com/blog</loc>
    <lastmod>2023-04-06T15:02:24.021Z</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.5</priority>
    </url>
    </urlset>
  2. 代码生成
    使用 sitemap.js / sitemap.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    export default function sitemap() {
    return [
    {
    url: 'https://acme.com',
    lastModified: new Date(),
    changeFrequency: 'yearly',
    priority: 1,
    },
    {
    url: 'https://acme.com/about',
    lastModified: new Date(),
    changeFrequency: 'monthly',
    priority: 0.8,
    },
    {
    url: 'https://acme.com/blog',
    lastModified: new Date(),
    changeFrequency: 'weekly',
    priority: 0.5,
    },
    ]
    }

metadata 属性

  1. title
    值可以是字符串,也可以是对象,使用对象时,default 为子路由没定义 title 的默认值,template 为子路由添加一个前缀或者后缀,并且使用 template 的话,必须同时 有 default。
    1
    2
    3
    4
    5
    6
    7
    8
    export const metadata = {
    title: {
    template: '%s | Acme', // %s 会被替换为子路由的 title
    default: 'Acme',

    absolute: 'About' // 设置标题,会忽略上层路由段设置的 title.template
    },
    }
  2. description
    设置页面的描述
  3. metadataBase
    设置 metadata 字段中地址的前缀
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // layout.js | page.js
    export const metadata = {
    metadataBase: new URL('https://acme.com'),
    alternates: {
    canonical: '/',
    languages: {
    'en-US': '/en-US',
    'de-DE': '/de-DE',
    },
    },
    openGraph: {
    images: '/og-image.png',
    },
    }
    输出结果
    1
    2
    3
    4
    <link rel="canonical" href="https://acme.com" />
    <link rel="alternate" hreflang="en-US" href="https://acme.com/en-US" />
    <link rel="alternate" hreflang="de-DE" href="https://acme.com/de-DE" />
    <meta property="og:image" content="https://acme.com/og-image.png" />
    使用 metadataBase 后,metadata 的地址还可以使用相对地址,Next 会进行自动合并,还会智能的处理多余的斜杠,当然如果 metadata 设置了绝对地址,metadataBase 会被忽略
  4. openGraph
    1
    2
    3
    4
    5
    6
    7
    8
    9
    export const metadata = {
    openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    type: 'article',
    publishedTime: '2023-01-01T00:00:00.000Z',
    authors: ['Seb', 'Josh'],
    },
    }
    输出
    1
    2
    3
    4
    5
    6
    <meta property="og:title" content="Next.js" />
    <meta property="og:description" content="The React Framework for the Web" />
    <meta property="og:type" content="article" />
    <meta property="article:published_time" content="2023-01-01T00:00:00.000Z" />
    <meta property="article:author" content="Seb" />
    <meta property="article:author" content="Josh" />
  5. robots
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // layout.js | page.js
    export const metadata = {
    robots: {
    index: false,
    follow: true,
    nocache: true,
    googleBot: {
    index: true,
    follow: false,
    noimageindex: true,
    'max-video-preview': -1,
    'max-image-preview': 'large',
    'max-snippet': -1,
    },
    },
    }
    输出的 HTML:
    1
    2
    3
    4
    5
    <meta name="robots" content="noindex, follow, nocache" />
    <meta
    name="googlebot"
    content="index, nofollow, noimageindex, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
    />

Next 元数据(metadata)
https://l1ushun.github.io/2024/08/30/next-04/
作者
liu shun
发布于
2024年8月30日