Menu

template.js

template 文件类似于 layout,它会包裹一个布局或页面。与跨路由持久化并维护状态的布局不同,模板会被赋予一个唯一的键,这意味着子客户端组件会在导航时重置其状态。

它们在以下情况下很有用:

  • 在导航时重新同步 useEffect
  • 在导航时重置子客户端组件的状态。例如,一个输入框。
  • 改变默认框架行为。例如,布局内的 Suspense 边界只在首次加载时显示后备内容,而模板在每次导航时都会显示。

约定

可以通过从 template.js 文件中导出一个默认的 React 组件来定义模板。该组件应接受一个 children prop。

template.js special file
app/template.tsx
TypeScript
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

在嵌套方面,template.js 在布局和其子元素之间渲染。这是一个简化的输出:

Output
<Layout>
  {/* 注意模板被赋予了一个唯一的键。 */}
  <Template key={routeParam}>{children}</Template>
</Layout>

Props

children(必需)

Template 接受一个 children prop。

Output
<Layout>
  {/* 注意模板会自动被赋予一个唯一的键。 */}
  <Template key={routeParam}>{children}</Template>
</Layout>

行为

  • 服务器组件:默认情况下,模板是服务器组件。
  • 导航时:模板会为其自己的段级别接收一个唯一的键。当该段(包括其动态参数)发生变化时,它们会重新挂载。在更深层段内的导航不会重新挂载更高级别的模板。搜索参数不会触发重新挂载。
  • 状态重置:模板内的任何客户端组件都会在导航时重置其状态。
  • 副作用重新运行:像 useEffect 这样的副作用会在组件重新挂载时重新同步。
  • DOM 重置:模板内的 DOM 元素会被完全重新创建。

导航和重新挂载期间的模板

本节说明了模板在导航期间的行为。它逐步展示了在每次路由变化时哪些模板会重新挂载以及原因。

使用这个项目树:

app
├── about
│   ├── page.tsx
├── blog
│   ├── [slug]
│   │   └── page.tsx
│   ├── page.tsx
│   └── template.tsx
├── layout.tsx
├── page.tsx
└── template.tsx

/ 开始,React 树大致如下所示。

注意:示例中显示的 key 值仅用于说明,你的应用程序中的值可能不同。

Output
<RootLayout>
  {/* app/template.tsx */}
  <Template key="/">
    <Page />
  </Template>
</RootLayout>

导航到 /about(第一段变化),根模板键发生变化,它重新挂载:

Output
<RootLayout>
  {/* app/template.tsx */}
  <Template key="/about">
    <AboutPage />
  </Template>
</RootLayout>

导航到 /blog(第一段变化),根模板键发生变化,它重新挂载并且博客级别的模板挂载:

Output
<RootLayout>
  {/* app/template.tsx (root) */}
  <Template key="/blog">
    {/* app/blog/template.tsx */}
    <Template key="/blog">
      <BlogIndexPage />
    </Template>
  </Template>
</RootLayout>

在同一第一段内导航到 /blog/first-post(子段变化),根模板键不变,但博客级别的模板键发生变化,它重新挂载:

Output
<RootLayout>
  {/* app/template.tsx (root) */}
  <Template key="/blog">
    {/* app/blog/template.tsx */}
    {/* 重新挂载,因为此级别的子段发生了变化 */}
    <Template key="/blog/first-post">
      <BlogPostPage slug="first-post" />
    </Template>
  </Template>
</RootLayout>

导航到 /blog/second-post(相同的第一段,不同的子段),根模板键不变,但博客级别的模板键发生变化,它再次重新挂载:

Output
<RootLayout>
  {/* app/template.tsx (root) */}
  <Template key="/blog">
    {/* app/blog/template.tsx */}
    {/* 由于子段变化再次重新挂载 */}
    <Template key="/blog/second-post">
      <BlogPostPage slug="second-post" />
    </Template>
  </Template>
</RootLayout>

版本历史

VersionChanges
v13.0.0引入 template