meta
On this page

meta

meta 导出允许你为应用中的每个路由添加元数据 HTML 标签。这些标签对于搜索引擎优化 (SEO) 和用于确定特定行为的浏览器指令等非常重要。社交媒体网站也可以使用它们来显示应用的丰富预览。

¥The meta export allows you to add metadata HTML tags for every route in your app. These tags are important for things like search engine optimization (SEO) and browser directives for determining certain behaviors. They can also be used by social media sites to display rich previews of your app.

meta 函数应该返回一个 MetaDescriptor 对象数组。这些对象与 HTML 标签一对一映射。所以这个元函数:

¥The meta function should return an array of MetaDescriptor objects. These objects map one-to-one with HTML tags. So this meta function:

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:

¥produces this 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> 标签。两个例外是:

¥By default, meta descriptors will render a <meta> tag in most cases. The two exceptions are:

  • { title } 渲染一个 <title> 标签。

    ¥{ title } renders a <title> tag

  • { "script:ld+json" } 渲染一个 <script type="application/ld+json"> 标签,其值应为可序列化的对象,该对象会被字符串化并注入到标签中。

    ¥{ "script:ld+json" } renders a <script type="application/ld+json"> tag, and its value should be a serializable object that is stringified and injected into the tag.

export const meta: MetaFunction = () => {
  return [
    {
      "script:ld+json": {
        "@context": "https://schema.org",
        "@type": "Organization",
        name: "Remix",
        url: "https://remix.nodejs.cn",
      },
    },
  ];
};

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

¥A meta descriptor can also render a <link> tag by setting the tagName property to "link". This is useful for <link> tags associated with SEO like canonical URLs. For asset links like stylesheets and favicons, you should use the links export instead.

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

meta 函数参数

¥meta Function Parameters

location

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

¥This is the current router Location object. This is useful for generating tags for routes at specific paths or query parameters.

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

matches

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

¥This is an array of the current route matches. You have access to many things, particularly the meta from the parent matches and data.

matches 的接口与 useMatches 的返回值类似,但每次匹配都会包含其 meta 函数的输出。这对 跨路由层次结构合并元数据 很有用。

¥The interface for matches is similar to the return value of useMatches, but each match will include the output of its meta function. This is useful for merging metadata across the route hierarchy.

data

这是来自路由 loader 的数据。

¥This is the data from your route's 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 参数。参见 动态路由指南中的段

¥The route's URL params. See Dynamic Segments in the Routing Guide.

error

触发错误边界的抛出错误将传递给 meta 函数。这对于生成错误页面的元数据很有用。

¥Thrown errors that trigger error boundaries will be passed to the meta function. This is useful for generating metadata for error pages.

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

从父路由加载器访问数据

¥Accessing Data from Parent Route Loaders

除了当前路由的数据外,你通常还需要访问路由层次结构中更高层级路由的数据。你可以通过 matches 中的路由 ID 进行查找。

¥In addition to the current route's data, often you'll want to access data from a route higher up in the route hierarchy. You can look it up by its route ID in matches.

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 和嵌套路由的陷阱

¥Gotchas with meta and Nested Routes

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

¥Because multiple nested routes render at the same time, there is some merging that needs to happen to determine the meta tags that ultimately render. Remix gives you complete control over this merge because there is no obvious default.

Remix 会使用最后一个匹配的 meta 导出路由并使用它。这允许你覆盖 title 之类的内容,删除父路由添加的 og:image 之类的内容,或者保留父路由中的所有内容并为子路由添加新的元数据。

¥Remix will take the last matching route with a meta export and use that. This allows you to override things like title, remove things like og:image that the parent route added, or keep everything from the parent and add new meta for the child route.

对于新手来说,这可能会非常棘手。

¥This can get quite tricky when you're new.

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

¥Consider a route like /projects/123, there are likely three matching routes: app/root.tsx, app/routes/projects.tsx, and app/routes/projects.$id.tsx. All three may export meta descriptors.

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 元标记,因为只使用了最后一个元标记,并且代码不会与父级合并。

¥With this code, we will lose the viewport meta tag at /projects and /projects/123 because only the last meta is used and the code doesn't merge with the parent.

全局 meta

¥Global meta

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

¥Nearly every app will have global meta like the viewport and charSet. We recommend using normal <meta> tags inside the root route instead of the meta export, so you don't have to deal with merging:

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

¥Avoid meta in Parent Routes

你也可以通过不导出要从父路由覆盖的 meta 来避免合并问题。不再在父路由上定义 meta,而是使用 索引路由。这样你就可以避免像标题这样复杂的合并逻辑。否则,你需要找到父级标题描述符并将其替换为子级标题。使用索引路由更容易,无需覆盖。

¥You can also avoid the merge problem by simply not exporting meta that you want to override from parent routes. Instead of defining meta on the parent route, use the index route. This way you can avoid complex merge logic for things like the title. Otherwise, you will need to find the parent title descriptor and replace it with the child's title. It's much easier to simply not need to override by using index routes.

与父 meta 合并

¥Merging with Parent meta

通常,你只需将 meta 添加到父级已定义的内容中即可。你可以使用扩展运算符和 matches 参数合并父 meta

¥Usually you only need to add meta to what the parent has already defined. You can merge parent meta with the spread operator and the matches argument:

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

请注意,这不会覆盖类似 title 的内容。这只是附加功能。如果继承的路由元数据包含 title 标签,你可以用 Array.prototype.filter 覆盖:

¥Note that this will not override something like title. This is only additive. If the inherited route meta includes a title tag, you can override with 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 Merging helper

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

¥If you can't avoid the merge problem with global meta or index routes, we've created a helper that you can put in your app that can override and append to parent meta easily.

Remix v2.17 中文网 - 粤ICP备13048890号