页面一多,最先开始重复的往往不是卡片,而是“上面那一块”。
当前站点里,下面这些页面都需要一个稳定的头部区块:
- 首页
- 文章列表页
- 系列列表页
- 归档页
- 关于页
- 系列详情页
- 文章/章节详情页外壳
如果每个页面都自己写一套 section.page-hero,很快就会出现这些问题:
- 返回链接有的放在 header 里,有的放在 section 里
- eyebrow、标题、补充文案的顺序不稳定
- 有的页面有分页状态,有的页面有摘要,模板结构越来越散
这次重构里,仓库把这层抽成了:
src/components/layout/PageHero.astro
组件输入只保留页面头部该关心的字段
PageHero 的输入刻意保持在“页面级头部”范围内:
type HeroLink = {
href: string;
label: string;
};
interface Props {
title: string;
intro?: string;
status?: string | null;
eyebrow?: string;
backLink?: HeroLink | null;
as?: "section" | "header" | "div";
className?: string;
}
这几个字段足够覆盖大多数页面头部:
title负责主标题intro负责导语status负责分页状态之类的补充文案eyebrow负责类别或上一级语义backLink负责返回入口as让它既能用于普通 section,也能用于语义更强的 header
详情页和索引页终于共用一套头部骨架
这个组件最有价值的地方,不是让首页更省代码,而是让“简单页面”和“详情页外壳”第一次共用了同一套头部结构。
例如系列详情页现在是:
<PageHero
as="header"
className="series-hero"
backLink={{ href: getSeriesPath(), label: copy.series.backToSeries }}
eyebrow={copy.series.heading}
title={series.data.title}
>
<p class="series-meta">{chapterCountLabel}</p>
{pageStatus ? <p class="series-meta">{pageStatus}</p> : null}
<p class="series-description">{series.data.description}</p>
</PageHero>
而文章详情页外壳也改成了同样的组合方式,只是 slot 里放的是元信息和摘要。
这意味着:
- 页面标题区的通用结构已经稳定
- 页面自己只需要关心“额外还要补什么”
- 以后再加新的详情页,不必重新写一套 header 模板
为什么要保留 slot
如果 PageHero 只支持固定字段,它最后会被迫不断长 props。
这次保留默认 slot 的目的,就是让它只负责:
- 标题区骨架
- 常见字段顺序
- 通用 back link / eyebrow 展示
而把“页面特有内容”留给调用方补进去。
这样:
- 首页只传
title + intro - 列表页传
title + status - 系列详情页传
backLink + eyebrow + slot - 文章外壳传
backLink + eyebrow + slot
组件本身不会因为个别页面需求而变成万能模板。
这次抽象解决的不是视觉,而是结构
PageHero 并没有发明新样式体系,它只是把已经存在的 page-hero 语义和顺序稳定下来。
真正的收益在于:
- 页面文件更短
- 页面头部结构更一致
- 返回链接、eyebrow、标题和分页状态不再各写一遍
- 详情页和索引页开始共享一套可靠的页面骨架