コンテンツにスキップ

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.run",
},
},
];
};

元描述符还可以通过将 tagName 属性设置为 "link" 来呈现 <link> 标签。这对于与 SEO 相关的 <link> 标签(如 canonical URL)非常有用。对于样式表和网站图标等资产链接,您应该改用 links 导出

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

meta 函数参数

location

这是当前路由器 Location 对象。这对于为特定路径或查询参数的路由生成标签很有用。

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

matches

这是当前路由匹配的数组。您可以访问许多内容,尤其是父匹配和数据的元数据。

matches 的接口类似于 useMatches 的返回值,但每个 match 都将包含其 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 查找。

// app/routes/project.$pid.tasks.$tid.tsx
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 和嵌套路由的陷阱

由于多个嵌套路由同时呈现,因此需要进行一些合并来确定最终呈现的元标记。Remix 让您完全控制此合并,因为没有明显的默认值。

Remix 将采用最后一个匹配的路由并导出元信息。这样您就可以覆盖 title 之类的内容,删除父路由添加的 og:image 之类的内容,或者保留父路由中的所有内容并为子路由添加新的元信息。

对于新手来说,这可能会变得相当棘手。

考虑像 /projects/123 这样的路由,可能有三条匹配的路由:app/root.tsxapp/routes/projects.tsxapp/routes/projects.$id.tsx。这三个路由都可能导出元描述符。

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

使用此代码,我们将丢失 /projects/projects/123 处的 viewport 元标记,因为仅使用最后一个元标记,并且代码不会与父级合并。

全局 meta

几乎每个应用程序都会有全局元数据,例如 viewportcharSet。我们建议在 根路由 内使用普通的 <meta> 标签,而不是 meta 导出,这样您就不必处理合并:

app/root.tsx
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 添加到父级已经定义的内容中。你可以将父级 meta 与扩展运算符和 matches 参数合并:

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

请注意,这不会覆盖 title 之类的内容。这只是附加的。如果继承的路由元数据包含 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 合并助手

如果您无法避免与全局元或索引路由合并的问题,我们已经创建了一个帮助程序,您可以将其放入您的应用程序中,它可以轻松覆盖和附加到父元。