React Router v7 已發布。 查看文件
meta
本頁內容

meta

meta 匯出允許您為應用程式中的每個路由新增中繼資料 HTML 標籤。這些標籤對於搜尋引擎最佳化 (SEO) 和瀏覽器指令以決定某些行為非常重要。它們也可以被社群媒體網站用來顯示您應用程式的豐富預覽。

meta 函式應傳回 MetaDescriptor 物件的陣列。這些物件與 HTML 標籤一一對應。因此,這個 meta 函式

export const meta: MetaFunction = () => {
  return [
    { title: "Very cool app | Remix" },
    {
      property: "og:title",
      content: "Very cool app",
    },
    {
      name: "description",
      content: "This app is the best",
    },
  ];
};

產生這個 HTML

<title>Very cool app | Remix</title>
<meta property="og:title" content="Very cool app" />;
<meta name="description" content="This app is the best" />

預設情況下,中繼描述符在大多數情況下會呈現一個 <meta> 標籤。兩個例外是

  • { title } 呈現一個 <title> 標籤
  • { "script:ld+json" } 呈現一個 <script type="application/ld+json"> 標籤,其值應為可序列化的物件,該物件會被字串化並注入標籤中。
export const meta: MetaFunction = () => {
  return [
    {
      "script:ld+json": {
        "@context": "https://schema.org",
        "@type": "Organization",
        name: "Remix",
        url: "https://remix.dev.org.tw",
      },
    },
  ];
};

中繼描述符也可以透過將 tagName 屬性設定為 "link" 來呈現一個 <link> 標籤。這對於與 SEO 相關的 <link> 標籤(例如 canonical URL)很有用。對於樣式表和網站圖示等資產連結,您應該改用links 匯出

export const meta: MetaFunction = () => {
  return [
    {
      tagName: "link",
      rel: "canonical",
      href: "https://remix.dev.org.tw",
    },
  ];
};

meta 函式參數

location

這是目前的路由器 Location 物件。這對於在特定路徑或查詢參數產生路由的標籤很有用。

export const meta: MetaFunction = ({ location }) => {
  const searchQuery = new URLSearchParams(
    location.search
  ).get("q");
  return [{ title: `Search results for "${searchQuery}"` }];
};

matches

這是目前路由匹配的陣列。您可以存取許多資訊,特別是來自父匹配的 meta 和 data。

matches 的介面類似於 useMatches 的返回值,但每個匹配都將包含其 meta 函數的輸出。這對於在路由層次結構中合併元數據很有用。

data

這是來自您路由的 loader 的資料。

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return json({
    task: await getTask(params.projectId, params.taskId),
  });
}

export const meta: MetaFunction<typeof loader> = ({
  data,
}) => {
  return [{ title: data.task.name }];
};

params

路由的 URL 參數。請參閱路由指南中的動態區段

error

觸發錯誤邊界的拋出錯誤將會傳遞到 meta 函數。這對於產生錯誤頁面的元數據很有用。

export const meta: MetaFunction = ({ error }) => {
  return [{ title: error ? "oops!" : "Actual title" }];
};

存取來自父路由載入器的資料

除了目前路由的資料外,您通常還會想要存取路由層次結構中較高層級的路由資料。您可以在 matches 中透過其路由 ID 進行查找。

import type { loader as projectDetailsLoader } from "./project.$pid";

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return json({ task: await getTask(params.tid) });
}

export const meta: MetaFunction<
  typeof loader,
  { "routes/project.$pid": typeof projectDetailsLoader }
> = ({ data, matches }) => {
  const project = matches.find(
    (match) => match.id === "routes/project.$pid"
  ).data.project;
  const task = data.task;
  return [{ title: `${project.name}: ${task.name}` }];
};

meta 與巢狀路由的陷阱

由於多個巢狀路由同時渲染,因此需要進行一些合併才能確定最終渲染的 meta 標籤。Remix 讓您可以完全控制此合併,因為沒有明顯的預設值。

Remix 將會採用最後一個匹配的具有 meta 輸出的路由並加以使用。這允許您覆寫諸如 title 之類的內容、移除父路由新增的諸如 og:image 之類的內容,或保留父路由的所有內容並為子路由新增新的 meta。

當您剛開始接觸時,這可能會變得非常棘手。

考慮像 /projects/123 這樣的路由,可能會有三個匹配的路由:app/root.tsxapp/routes/projects.tsxapp/routes/projects.$id.tsx。所有三個路由都可能輸出 meta 描述符。

export const meta: MetaFunction = () => {
  return [
    {
      name: "viewport",
      content: "width=device-width,initial-scale=1",
    },
    { title: "New Remix App" },
  ];
};
export const meta: MetaFunction = () => {
  return [{ title: "Projects" }];
};
export const meta: MetaFunction<typeof loader> = ({
  data,
}) => {
  return [{ title: data.project.name }];
};

使用此程式碼,我們將會在 /projects/projects/123 遺失 viewport meta 標籤,因為只使用最後一個 meta,並且該程式碼不會與父元素合併。

全域 meta

幾乎每個應用程式都會有像 viewportcharSet 這樣的全域 meta。我們建議在 根路由 中使用一般的 <meta> 標籤,而不是 meta 輸出,這樣您就不需要處理合併的問題。

import {
  Links,
  Meta,
  Outlet,
  Scripts,
} from "@remix-run/react";

export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}

避免在父路由中使用 meta

您還可以透過簡單地不輸出您想要從父路由覆寫的 meta 來避免合併問題。不要在父路由上定義 meta,而是使用索引路由。這樣您就可以避免針對標題等內容進行複雜的合併邏輯。否則,您需要找到父標題描述符,並將其替換為子標題。透過使用索引路由來避免需要覆寫,會容易得多。

與父 meta 合併

通常您只需要將 meta 新增到父路由已定義的內容。您可以使用展開運算符和 matches 參數來合併父 meta

export const meta: MetaFunction = ({ matches }) => {
  const parentMeta = matches.flatMap(
    (match) => match.meta ?? []
  );
  return [...parentMeta, { title: "Projects" }];
};

請注意,這不會覆寫諸如 title 之類的內容。這只是附加性的。如果繼承的路由 meta 包含 title 標籤,您可以使用 Array.prototype.filter 來覆寫。

export const meta: MetaFunction = ({ matches }) => {
  const parentMeta = matches
    .flatMap((match) => match.meta ?? [])
    .filter((meta) => !("title" in meta));
  return [...parentMeta, { title: "Projects" }];
};

meta 合併輔助程式

如果您無法透過全域 meta 或索引路由來避免合併問題,我們建立了一個輔助程式,您可以將其放入您的應用程式中,以便輕鬆覆寫和附加到父 meta。

文件和範例根據授權 MIT