Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

文档头部管理 (Document Head Management)

文档头部管理是指管理文档中的 headtitlemetalinkscript 标签的过程。TanStack Router 为使用 Start 的全栈应用以及使用 TanStack Router 的单页应用(SPA)提供了一套强大的管理方案。其特性包括:

无论是对于全栈应用还是单页应用,管理文档头部都是至关重要的,原因如下:

要管理文档头部,必须渲染 <HeadContent /><Scripts /> 组件,并使用 routeOptions.head 属性。该属性返回一个包含 titlemetalinksstylesscripts 属性的对象。

管理文档头部

export const Route = createRootRoute({
  head: () => ({
    meta: [
      {
        name: "description",
        content: "我的应用是一个 Web 应用程序",
      },
      {
        title: "我的应用",
      },
    ],
    links: [
      {
        rel: "icon",
        href: "/favicon.ico",
      },
    ],
    styles: [
      {
        media: "all and (max-width: 500px)",
        children: `p {
                  color: blue;
                  background-color: yellow;
                }`,
      },
    ],
    scripts: [
      {
        src: "https://www.google-analytics.com/analytics.js",
      },
    ],
  }),
});

去重机制 (Deduping)

TanStack Router 开箱即支持对 titlemeta 标签进行去重,并遵循后到优先原则(即优先使用嵌套路由中最后发现的标签)。

<HeadContent />

渲染文档的头部、标题、元数据、链接和头部相关的脚本标签必须使用 <HeadContent /> 组件。

它应该在根布局的 <head> 标签中渲染;如果你的应用无法直接管理 <head> 标签,则应尽可能渲染在组件树的顶端。

Start/全栈应用示例

import { HeadContent } from "@tanstack/react-router";

export const Route = createRootRoute({
  component: () => (
    <html>
      <head>
        <HeadContent />
      </head>
      <body>
        <Outlet />
      </body>
    </html>
  ),
});

单页应用 (SPA) 示例

首先,如果你的 index.html 中设置了 <title> 标签,请将其移除。

import { HeadContent } from "@tanstack/react-router";

const rootRoute = createRootRoute({
  component: () => (
    <>
      <HeadContent />
      <Outlet />
    </>
  ),
});

管理 Body 脚本

除了可以在 <head> 中渲染脚本外,你还可以使用 routeOptions.scripts 属性在 <body> 标签中渲染脚本。这对于加载那些需要 DOM 已加载、但在应用主入口点(包括 Start 或全栈实现中的注水/Hydration)运行之前的脚本(甚至是内联脚本)非常有用。

操作步骤:

export const Route = createRootRoute({
  scripts: () => [
    {
      children: 'console.log("Hello, world!")',
    },
  ],
});

<Scripts />

渲染文档的 body 脚本必须使用 <Scripts /> 组件。它应该在根布局的 <body> 标签中渲染,或者在应用无法管理 <body> 标签时,尽可能渲染在组件树的高处。

示例

import { createRootRoute, Scripts } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
  component: () => (
    <html>
      <head />
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  ),
});

使用 ScriptOnce 渲染内联脚本

对于必须在 React 注水(Hydration)之前运行的脚本(例如主题检测),请使用 ScriptOnce。这对于避免样式闪烁(FOUC)或主题切换闪烁特别有效。

import { ScriptOnce } from "@tanstack/react-router";

const themeScript = `(function() {
  try {
    const theme = localStorage.getItem('theme') || 'auto';
    const resolved = theme === 'auto'
      ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
      : theme;
    document.documentElement.classList.add(resolved);
  } catch (e) {}
})();`;

function ThemeProvider({ children }) {
  return (
    <>
      <ScriptOnce children={themeScript} />
      {children}
    </>
  );
}

ScriptOnce 的工作原理

  1. SSR 期间:渲染一个包含所提供代码的 <script> 标签。

  2. 浏览器解析:当浏览器解析 HTML 时,脚本立即执行(在 React 注水之前)。

  3. 自我销毁:执行完毕后,脚本会从 DOM 中自行移除。

  4. 客户端导航:在后续的客户端导航中,该组件不会渲染任何内容(防止重复执行)。

防止注水警告

如果你的脚本在注水前修改了 DOM(例如向 <html> 添加类名),请使用 suppressHydrationWarning 来防止 React 抛出警告:

export const Route = createRootRoute({
  component: () => (
    <html lang="en" suppressHydrationWarning>
      <head>
        <HeadContent />
      </head>
      <body>
        <ThemeProvider>
          <Outlet />
        </ThemeProvider>
        <Scripts />
      </body>
    </html>
  ),
});

常见用例