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.org.cn",
},
},
];
};
元描述符还可以通过将 tagName
属性设置为 "link"
来呈现一个 <link>
标签。这对与 SEO 相关的 <link>
标签很有用,例如 canonical
URL。对于像样式表和网站图标这样的资产链接,您应该使用 links
导出。
export const meta: MetaFunction = () => {
return [
{
tagName: "link",
rel: "canonical",
href: "https://remix.org.cn",
},
];
};
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
的返回值类似,但每个匹配项都将包含其 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
和嵌套路由的注意事项因为多个嵌套路由同时渲染,所以需要进行一些合并以确定最终渲染的元标签。Remix 使你能够完全控制此合并,因为没有明显的默认值。
Remix 将采用具有 meta 导出的最后一个匹配路由并使用它。这使你能够覆盖诸如 title
之类的内容,删除诸如父路由添加的 og:image
之类的内容,或保留父路由的所有内容并为子路由添加新的元数据。
当你刚开始使用时,这可能会很棘手。
考虑一个像 /projects/123
这样的路由,可能有三个匹配路由:app/root.tsx
、app/routes/projects.tsx
和 app/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
几乎每个应用程序都将具有全局元数据,例如 viewport
和 charSet
。我们建议在 根路由 中使用正常的 <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。