跳至內容

詮釋資料

Next.js 有一個詮釋資料 API,可用於定義應用程式詮釋資料(例如 HTML head 元素內的 metalink 標籤),以改善 SEO 和網頁分享能力。

您可以透過兩種方式將詮釋資料新增至您的應用程式

  • 基於設定的詮釋資料:在 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: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // read route params
  const id = (await params).id
 
  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json())
 
  // optionally access and extend (rather than replace) parent 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) {}

關於所有可用的參數,請參閱 API 參考

注意事項:

  • 透過 generateMetadata 產生的靜態和動態詮釋資料都僅在伺服器元件中支援
  • 針對 generateMetadatagenerateStaticParams、佈局、頁面和伺服器元件之間的相同資料,fetch 請求會自動進行 記憶體快取。如果 fetch 無法使用,則可以使用 React 的 cache 函式
  • Next.js 會等待 generateMetadata 內的資料提取完成,才會將 UI 串流到用戶端。這可確保 串流回應 的第一部分包含 <head> 標籤。

基於檔案的詮釋資料

以下特殊檔案可用於詮釋資料

您可以將這些檔案用於靜態詮釋資料,也可以使用程式碼以程式設計方式產生這些檔案。

關於實作和範例,請參閱 詮釋資料檔案 API 參考和 動態圖片產生

行為

基於檔案的詮釋資料具有較高的優先順序,並且會覆寫任何基於設定的詮釋資料。

預設欄位

即使路由沒有定義詮釋資料,也始終會新增兩個預設的 meta 標籤

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

貼心小提醒:您可以覆寫預設的 viewport meta 標籤。

順序

中繼資料會依順序進行評估,從根區段開始,向下到最接近最終 page.js 區段的區段。例如:

  1. app/layout.tsx(根佈局)
  2. app/blog/layout.tsx(巢狀部落格佈局)
  3. 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',
  },
}
 
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

在上述範例中:

  • app/layout.js 中的 title 會被 app/blog/page.js 中的 title **取代**。
  • app/layout.js 中的所有 openGraph 欄位都會在 app/blog/page.js 中被**取代**,因為 app/blog/page.js 設定了 openGraph 中繼資料。請注意缺少 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',
  },
}

在上述範例中,OG 圖片在 app/layout.jsapp/about/page.js 之間共享,而標題則不同。

欄位繼承

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/about/page.js
export const metadata = {
  title: 'About',
}
 
// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

注意事項

  • app/layout.js 中的 title 會被 app/about/page.js 中的 title **取代**。
  • 由於 app/about/page.js 沒有設定 openGraph 中繼資料,所有 app/layout.js 中的 openGraph 欄位都會被 app/about/page.js **繼承**。

動態圖片產生

ImageResponse 建構函式允許您使用 JSX 和 CSS 產生動態圖片。這對於建立社群媒體圖片(例如 Open Graph 圖片、Twitter 卡片等)非常有用。

要使用它,您可以從 next/og 導入 ImageResponse

app/about/route.js
import { ImageResponse } from 'next/og'
 
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 圖片。

ImageResponse 支援常見的 CSS 屬性,包括 flexbox 和絕對定位、自訂字體、文字換行、置中和巢狀圖片。查看支援的 CSS 屬性完整清單

注意事項:

  • 範例可在Vercel OG Playground中找到。
  • ImageResponse 使用@vercel/ogSatori 和 Resvg 將 HTML 和 CSS 轉換為 PNG。
  • 僅支援 Edge Runtime。預設的 Node.js 執行環境將無法運作。
  • 僅支援 flexbox 和部分 CSS 屬性。進階排版(例如 display: grid)將無法運作。
  • 最大 bundle 大小為 500KB。bundle 大小包含您的 JSX、CSS、字體、圖片和任何其他資源。如果超過限制,請考慮減少任何資源的大小或在執行階段擷取。
  • 僅支援 ttfotfwoff 字體格式。為了最大限度地提高字體解析速度,建議使用 ttfotf,而不是 woff

JSON-LD

JSON-LD 是一種結構化資料格式,搜尋引擎可以使用它來理解您的內容。例如,您可以使用它來描述人物、事件、組織、電影、書籍、食譜和許多其他類型的實體。

我們目前建議您在 layout.jspage.js 元件中將 JSON-LD 結構化資料渲染為 <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>
      {/* Add JSON-LD to your page */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

您可以使用 Google 的 富媒體搜尋結果測試 或通用的 Schema 標記驗證器 來驗證和測試您的結構化資料。

您可以使用像 schema-dts 這樣的社群套件,使用 TypeScript 撰寫您的 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.dev.org.tw/imgs/sticker.png',
  description: 'Dynamic at the speed of static.',
}