我正在开发一个具有嵌套文件结构的 Next.js 项目,我希望每个布局级别递归地添加标题前缀,因此最终标题是直到当前页面的所有布局标题的组合。
这是我的文件夹结构:
├── app
│ ├── layout.tsx // Title "Studio"
│ ├── project-review
│ │ ├── layout.tsx // Title "Studio - Project Review"
│ │ ├── history
│ │ │ └── page.tsx // // Title "Studio - Project Review - history"
│ │ └── page.tsx
│ └── page.tsx
在我的根布局.tsx 中,我设置了基本标题模板:
// app/layout.tsx
export const metadata = {
title: { template: 'Studio - %s', default: 'Studio' },
};
在下一个文件中:
// app/project-review/layout.tsx
export const metadata = {
title: { template: 'Project Review - %s', default: 'Project reviews' },
};
// app/project-review/history/page.tsx
export const metadata = {
title: { default: 'History' },
};
我需要将标题 teplmate 中的值链接到所有嵌套布局
通过此设置,显示的标题为项目回顾 - 历史记录,而不是 Studio - 项目回顾 - 历史记录。布局中的每个元数据对象似乎都会替换而不是附加到先前的元数据,因此仅使用最近的标题模板。
为什么这种方法不能满足我的需求
我的目标是让 Next.js 自动组合嵌套布局中的标题,而无需在每个布局中手动连接它们。这将允许采用可扩展的方法来管理复杂的页面标题,这些标题动态地反映我的应用程序的结构,其中每个布局都有自己独特的前缀。
有没有办法在 Next.js 中使用元数据实现递归标题模板,或者我需要一个自定义解决方案来手动处理这种继承吗?
元数据仅影响其正下方的页面,这解释了为什么最近的标题模板会覆盖前面的页面。
但是您可以使用 Context 来跟踪标题,请参阅下面的示例
首先,在应用程序文件夹中创建一个上下文文件(TitleContext.tsx)
import React, { createContext, useContext, useMemo } from 'react';
const TitleContext = createContext<string[]>([]);
export const useTitleSegments = () => useContext(TitleContext);
export const TitleProvider = ({ segment, children }: { segment: string, children: React.ReactNode }) => {
const parentSegments = useTitleSegments();
const segments = useMemo(() => [...parentSegments, segment], [parentSegments, segment]);
return (
<TitleContext.Provider value={segments}>
{children}
</TitleContext.Provider>
);
};
然后更新您的 layout.tsx 将子项包装在标题提供程序中
import { TitleProvider } from './TitleContext';
export const metadata = {
title: { template: 'Studio - %s', default: 'Studio' },
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<TitleProvider segment="Studio">
{children}
</TitleProvider>
);
}
接下来在各个页面布局页面中一起添加标题,例如 project-review/history/page.tsx
import { useTitleSegments } from '../../TitleContext';
export const metadata = {
title: { default: 'History' },
};
export default function HistoryPage() {
const titleSegments = useTitleSegments();
const title = titleSegments.join(' - '); // "Studio - Project Review - History"
// Use this title for dynamic head or pass it as metadata if needed.
return (
<>
<head>
<title>{title}</title>
</head>
<main>
<h1>{title}</h1>
{/* Page content */}
</main>
</>
);
}