内容、数据与页面联动关系
很多新手会遇到这种困惑:我已经在 docs/posts/ 里创建了 Markdown,为什么首页没有卡片?为什么目录页不显示?为什么能打开文章却搜不到?原因通常不是文章不存在,而是不同页面由不同的加载器读取不同字段。
这一篇把整条链路拆开:源文件如何变成路由,frontmatter 如何影响各个页面,哪些输出是自动的,哪些配置必须手工同步。读完后,你应该能根据「缺失的位置」反推应该检查哪个文件。
从一份 Markdown 到多个页面
一条文章数据的完整传播大致如下:
docs/posts/<分类>/<文章>.md
├─ permalink 插件 / VitePress rewrites ──→ 页面路由、canonical、sitemap
├─ Teek 文章数据加载 ──→ 首页文章流、分类、标签、归档、精选、置顶
├─ docs/.vitepress/sidebar.ts ──→ 对应分类的文章左侧栏
├─ docs/.vitepress/search-manifest.ts ──→ docs/public/search-manifest.json
│ └─→ /search 自定义搜索的目录分组和顺序
├─ Pagefind 构建 ──→ docs/.vitepress/dist/pagefind/
├─ RSS 插件 ──→ docs/.vitepress/dist/feed.rss
├─ VitePress sitemap ──→ docs/.vitepress/dist/sitemap.xml
└─ config.ts transformHead ──→ title、description、OG、canonical、JSON-LD目录页的数据流不同:
docs/catalog/<镜像路径>.md
├─ layout: page
├─ catalogue: true
└─ path: posts/<真实文章目录>
└─ Teek 目录插件扫描 docs/posts 下对应目录
└─ 生成目录页中的文章列表目录页不读取 docs/posts 里的 index.md 来充当自身内容,也不需要自己维护文章 HTML。它的标题和描述来自自己的 frontmatter,列表来自 path 指向的目录。
谁读取了什么
| 文件或机制 | 读取范围 | 产生的结果 | 新手要记住的点 |
|---|---|---|---|
docs/.vitepress/sidebar.ts | docs/posts/ 下的 Markdown | 按分类生成文章左侧栏 | 跳过 index.md、以 _ 或 . 开头的条目,以及 sidebar: false 的文章;侧边栏扫描不读取 article 字段 |
docs/.vitepress/search-manifest.ts | docs/posts/ 下的 Markdown | 生成 docs/public/search-manifest.json | 只扫描 posts,不扫描 catalog 和 writing-guide;跳过 index 和 article false |
| Teek 文章数据加载 | Markdown frontmatter 和正文 | 首页文章流、分类、标签、归档、精选和置顶 | article: false 会被排除;date、sticky、top 在这里生效 |
| Teek catalogue 插件 | 目录页的 path | 目录页中的文章列表 | path 是相对 docs/ 的 posts 目录,不是 URL |
| Pagefind | 构建后的页面 | docs/.vitepress/dist/pagefind/ 搜索索引 | pagefind-indexed: false 的页面被跳过 |
| 自定义搜索页 | Pagefind 结果 + search-manifest.json | /search 的目录树、文章分组和排序 | Pagefind 负责「搜得到内容」,manifest 负责「如何分组和排序」 |
| RSS 插件 | 文章数据 | docs/.vitepress/dist/feed.rss | article: false 以及路径含「写作规范」的文章被过滤 |
| sitemap | 构建后的路由 | docs/.vitepress/dist/sitemap.xml | 只排除 noindexPaths 中固定的功能页,不等于 Pagefind 索引开关 |
config.ts 的 transformHead | 页面 frontmatter 和 lastUpdated | title、description、OG、canonical、文章 JSON-LD | 没有显式 permalink 时 canonical 可能落到错误地址 |
Frontmatter 字段会影响哪里
下表以 docs/posts/ 中的正式文章为基准。目录页专有的 layout、catalogue、path、pageTitle 见目录页完整流程。
| 字段 | 推荐写法 | 会影响的页面或系统 | 说明 |
|---|---|---|---|
title | 中文标题,与 H1 一致 | 页面标题、浏览器标题、SEO title、侧边栏文字、首页卡片、搜索结果标题 | 侧边栏在没有单独配置时优先读它;文章也用它生成主题文章数据。 |
description | 一句话说清内容,50 至 150 个中文字符 | meta description、OG/Twitter 描述、首页卡片摘要 | 不要写成关键词堆砌。自定义搜索的正文摘要来自 Pagefind,不依赖这个字段;目录页至少写 description;旧目录页可以保留同值的 desc 供目录组件显示,但 desc 不是 SEO 主字段。 |
date | 通常省略;文件第一次 Git commit 后自动写入 YYYY-MM-DD HH:mm:ss | 首页文章流顺序、归档、分类/标签中的顺序、页面日期显示、文章发布时间 SEO | 新建文章不要手填日期;构建初始化器会读取文件首次 commit 的作者时间并保留。文件尚未提交时 Teek 用文件创建时间临时兜底,提交后再次构建即可固化。 |
permalink | /posts/...,显式且唯一 | 页面 URL、canonical、OG URL、侧边栏链接、搜索清单 route、sitemap、RSS 链接 | 这是公开地址的真正来源。当前 autoFrontmatter 配置会跳过已有 frontmatter 的普通文章,不能依赖自动生成;必须显式写。 |
categories | 一级分类数组,例如 技术笔记 | /categories 分类汇总、文章卡片分类、按分类筛选、主题文章元数据 | 约定与 docs/posts/ 的一级目录一致;一篇可以属于多个分类,但不要随手制造近似重复分类。 |
tags | 主题标签数组 | /tags 标签汇总、文章标签展示、SEO keywords、article:tag | 适合具体主题;同一概念保持统一写法,不要把「VitePress」「vitepress」当成两个标签。 |
coverImg | 需要固定封面时写 /images/posts/<slug>/cover.webp 或 /home/bgXX.webp;否则省略 | 首页卡片封面、文章 Banner、OG image、微信/Twitter 分享图 | 首页卡片优先级:显式 coverImg → 正文图片随机 → /home/bg*.webp 随机。不写 coverImg 不会让首页卡片缺图,但 SEO 分享图会退回站点默认图。 |
article | 正式文章 true | 首页文章流、分类、标签、归档、RSS、主题文章数据和 search-manifest(不影响侧边栏扫描) | false 会被主题文章过滤器、RSS 和 search-manifest 排除;它不会直接关闭左侧栏或目录扫描,那两项分别看 sidebar 和 inCatalogue。它不是删除按钮,页面仍可能按路由访问。 |
sidebar | 正式文章 true,目录/说明页 false | 文章左侧栏 | sidebar.ts 遇到字符串 false 会跳过该文章;它不控制 Pagefind 或 sitemap。 |
sidebarSort | 正整数,可选 | 文章在侧边栏和 search-manifest.json 中的顺序 | 优先级高于文件名的 NN. 前缀;数字越小越靠前。不要用 0 或负数,它们会被当作未设置。 |
pagefind-indexed | 正式文章 true,目录/说明页 false | Pagefind 搜索索引 | 只有明确为 false 时才跳过;它不影响 sitemap,也不等于 SEO noindex。 |
comment | 正式文章 true,目录/说明页 false | 页面评论区 | 是否真正显示还取决于评论 provider 配置;关闭时页面不会出现评论入口。 |
top | top: 1,可选 | 首页精选文章卡片 | 值为真即可进入精选候选,主题配置 topArticle.limit 为 4。它不负责普通文章流排序。 |
sticky | 1 或其他正整数,可选 | 首页及分类/标签文章流的置顶顺序 | sticky 文章排在非 sticky 前;多个 sticky 按数值升序,同值再按日期。它不影响侧边栏。 |
inHomePost | 通常省略;不想进首页主列表时写 false | 首页主文章列表 | 影响首页、分类页和标签页等 HomePost 列表;不会从精选、归档或搜索中自动删除,也不是隐私开关。 |
inCatalogue | 通常省略;不想让文章出现在目录页时写 false | Teek 目录扫描 | 只影响 path 扫描出的目录页条目,不影响侧栏、Pagefind、首页或归档。 |
titleTag | 简短徽标文字,例如 实战 | 文章列表标题旁的标签 | 是展示徽标,不改变分类或标签数据,也不应代替 tags。 |
另外有一个可选字段 updated。它不是日常更新文章时必须改的字段。config.ts 的 transformHead 会把它作为 article:modified_time 和 JSON-LD dateModified 的优先来源;页面上的更新时间仍主要来自 VitePress 的 lastUpdated 和 Git 记录。不要为了显得「最近更新」而伪造 updated。
内容类型与各输出面的对应关系
下面这张表回答「这种页面会出现在哪里」。
| 内容类型 | 首页主列表 | 精选卡片 | 左侧栏 | Pagefind | 评论 | 分类/标签/归档 | RSS | sitemap |
|---|---|---|---|---|---|---|---|---|
正式文章,article: true、pagefind-indexed: true | 是(除非 inHomePost: false) | 条件(top 为真) | 是(除非 sidebar: false) | 是 | 条件(comment: true) | 是 | 是 | 是 |
正式文章但 inHomePost: false | 否 | 仍可能按 top 出现 | 是 | 是 | 条件 | 是 | 是 | 是 |
posts 下 article: false 的隐藏页 | 否 | 否 | 否 | 看 pagefind-indexed | 看 comment | 否 | 否 | 是(除非命中固定 noindex 路径) |
目录页 layout: page、catalogue: true | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 是 |
| 写作指南和模板说明页 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 是 |
固定 noindex 功能页(如 /search) | 否 | 否 | 否 | 看插件过滤 | 看页面配置 | 否 | 否 | 否 |
这里最容易误解的是 Pagefind 与 sitemap 不是同一件事。写作指南页面设置 pagefind-indexed: false,只是不进入站内搜索;VitePress 仍会把它们写进 sitemap,除非它属于 config.ts 中写死的 noindexPaths。noindexPaths 当前包含 /search、/message-area、/friend-links、/navigation、/risk-link。这个数组控制 robots 和 sitemap,不是 Pagefind 的开关。
search-manifest 与 Pagefind 各自做什么
两者名字都像「搜索」,但职责不同。
docs/.vitepress/search-manifest.ts 在构建或开发服务器启动时扫描 docs/posts/,生成 docs/public/search-manifest.json。它记录每篇文章的标题、route、源文件、所在目录数组和文章顺序,也记录目录层级。自定义搜索页 /search 会读取这份文件,把 Pagefind 命中的文章放进正确的目录树,并按 sidebarSort 或文件名编号排序。
这个清单的扫描规则很重要:
- 只扫描
docs/posts/,不扫描docs/catalog/,也不扫描docs/writing-guide/。 - 跳过
index.md、以_或.开头的文件/目录。 - 排除
article: false的文章。 - 一级目录顺序来自
search-manifest.ts中的categoryOrder。 - 文章顺序优先使用正整数
sidebarSort,否则使用文件名的数字前缀。 - 生成结果被
.gitignore忽略,因为它是扫描产物,而且source中包含本机绝对路径,不适合在不同环境之间提交。
Pagefind 则是真正的全文搜索索引。它在构建时扫描可索引页面,写入 docs/.vitepress/dist/pagefind/;页面上明确写 pagefind-indexed: false 才会被跳过。Pagefind 决定「搜不搜得到正文」,search-manifest 决定「结果如何归类和排序」。如果 Pagefind 能搜到但目录树不对,先怀疑 manifest;如果完全搜不到,先检查 pagefind-indexed 和构建后的搜索索引。
目录页和写作指南不是文章:它们不进入 search-manifest,目录页还明确设置 pagefind-indexed: false。这正是目录页只在目录导航中出现、而不会混入文章搜索的原因。
生成文件绝对不能手改
| 路径 | 由谁生成 | 为什么不能手改 | Git 状态 |
|---|---|---|---|
docs/.vitepress/dist/ | pnpm docs:build | 整个站点部署产物;下次构建会覆盖 | 被 dist 规则忽略 |
docs/.vitepress/dist/pagefind/ | Pagefind 构建插件 | 全文搜索索引,手改无效且会损坏搜索 | 随 dist 忽略 |
docs/.vitepress/dist/feed.rss | RSS 插件 | 由文章数据生成,手改会在下次构建丢失 | 随 dist 忽略 |
docs/.vitepress/dist/sitemap.xml | VitePress sitemap | 路由和 transformItems 自动生成 | 随 dist 忽略 |
docs/.vitepress/cache/ | VitePress/Vite | 开发缓存,删除后可重建;手改只会制造怪问题 | 被 /**/.vitepress/cache/* 忽略 |
docs/public/search-manifest.json | plugins.ts 启动时写入 | 每次启动由 search-manifest.ts 覆盖;内容含本机绝对路径 | .gitignore 明确忽略 |
stats.html | 包体分析工具 | 构建分析报告,不是站点页面源码 | .gitignore 明确忽略 |
为什么这些文件要忽略?因为它们都能从源文件重新生成。提交它们会造成三类问题:第一,不同电脑的绝对路径、时间和压缩结果不同,产生无意义 diff;第二,构建前的旧产物可能让人误以为页面已更新;第三,多个分支修改同一生成文件时容易冲突。真正的修复入口永远是 docs/posts、docs/catalog、静态资源或对应的 .ts 配置。
注意 docs/public/ 中绝大多数文件是源静态资源,但 search-manifest.json 是一个明确的例外。不要因为它在 public 下就把它当手工文件编辑。判断方法不是看目录名,而是看它是否由脚本自动写出、是否在 .gitignore 中。 还有一个容易混淆的配置是 config.ts 里的 docAnalysisOption.ignoreList。当前忽略列表是 ["pages", "catalog", "writing-guide", "search.md"],因此目录页、写作指南和 pages 目录不会进入主题的文档统计。这个设置只影响统计口径,不等于禁止访问,也不等于 Pagefind 或 sitemap 的开关。写作指南页面仍然需要自己明确设置 article: false 和 pagefind-indexed: false。
哪些地方会自动传播
只要源文件和字段正确,保存后或重启开发服务器,下列内容通常会自动更新:
- 文章的公共路由和 canonical 来自
permalink。 - 同一分类目录下的目录页通过
catalogue和path自动列出新文章。 sidebar.ts重新扫描docs/posts,新文章自动出现在对应分类的左侧栏中。- Teek 文章数据重新加载,首页文章流、
/categories、/tags、/archives自动汇总。 top和sticky自动影响精选卡片和置顶顺序。pagefind-indexed: true的文章在构建后进入 Pagefind 全文索引。search-manifest.json在开发服务器或构建启动时重新生成,/search的目录分组会随之变化。- RSS 和 sitemap 在下一次构建时根据文章和路由自动生成。
transformHead根据 title、description、coverImg、tags 和日期自动生成 SEO/社交分享元数据;未写coverImg时分享图退回站点默认图,首页卡片的随机封面不会改变 SEO 分享图。- VitePress 的
lastUpdated: true在 Git 历史可用时,读取同一文件最近一次 Git commit 的作者时间作为页面更新时间;创建时间则由article-date.ts读取第一次 commit 的作者时间。正常完整历史下,更新时间不会早于创建时间。
哪些地方必须手工同步
| 需要同步的内容 | 位置 | 什么时候需要 |
|---|---|---|
| 顶部导航入口 | docs/.vitepress/ConfigHyde/Nav.ts | 新增一级分类、想把某个目录/页面放进顶部导航时;目录页不会自动注册导航 |
| 首页 Banner 功能卡片 | docs/.vitepress/ConfigHyde/Features.ts | 需要把某个目录做成首页功能入口时;文章和目录不会自动出现在这里 |
| 一级分类顺序 | docs/.vitepress/sidebar.ts 和 docs/.vitepress/search-manifest.ts 的 categoryOrder | 新增、删除或调整一级分类顺序时;两处必须同时改 |
| 二级目录顺序 | docs/posts/ 下的数字前缀目录名 | 需要控制二级目录在侧边栏/搜索清单中的顺序时;不需要改 categoryOrder |
| 手写站内链接 | 各 Markdown 页面、导航、Features | 文章标题、目录、permalink 改变时;自动扫描不会替你修复普通 Markdown 链接 |
| 旧 URL 重定向 | 托管平台或 EdgeOne 配置 | permalink 或目录 URL 改变后;静态站没有自动重定向能力 |
| 目录页本身 | docs/catalog/... | 新增、删除目录时;文章自动出现不等于目录入口自动存在 |
| 评论、统计等外部配置 | 对应配置文件和平台后台 | 不属于文章内容联动;需要按服务文档单独维护 |
一句话总结:文章内容在已有结构中主要自动传播;结构入口、顺序和外部链接需要人工维护。
新增一篇文章时到底会改变哪些输出
假设把下面这篇文章加入已有的「技术笔记 / 项目记录」目录:
docs/posts/技术笔记/项目记录/02.vitepress-audit.mdfrontmatter 为:
---
title: VitePress 上线前 SEO 审计实践
description: 记录从构建、审计到预览验收的完整检查流程。
permalink: /posts/技术笔记/项目记录/vitepress-audit
categories:
- 技术笔记
tags:
- VitePress
- SEO
article: true
sidebar: true
pagefind-indexed: true
comment: true
---一次正确保存后,预期会发生:
- 新路由
/posts/技术笔记/项目记录/vitepress-audit可访问,canonical 使用该地址。 docs/catalog/技术笔记/项目记录.md的目录列表出现新文章。- 技术笔记侧边栏的「项目记录」分组出现新条目,顺序受
02.或sidebarSort影响。 - 首页文章流出现卡片;封面、标题、描述和日期来自 frontmatter。
/categories的「技术笔记」计数和列表变化。/tags的「VitePress」「SEO」标签计数和列表变化。/archives按2026-11-08归档到对应年月。- 自定义
/search可以从 Pagefind 找到正文;search-manifest 生成新条目并参与目录分组。 - 构建后的
feed.rss和sitemap.xml包含这篇文章,前提是字段与 RSS 过滤条件允许。 - 页面
<head>中的 description、OG image、article:published_time、article:tag 和 JSON-LD 更新。 - Git 提交后,若页面启用
lastUpdated,更新时间来自 Git 记录。
如果你只新增文章且目录已经存在,通常不需要改 Nav.ts、Features.ts 或 categoryOrder。但如果你发现顶部导航或首页 Banner 没有出现它,这是正常的:这两处本来就是手工维护。
没有出现在预期位置时查哪里
| 症状 | 最可能出问题的层 | 按顺序检查 |
|---|---|---|
| 文章 URL 404 | 路由 | permalink 是否显式、是否以 /posts/ 开头、URL 是否误加 .html、开发服务器是否启动或重启 |
打开的是旧 /pages/xxxxxx | 历史自动地址或缺少 permalink | 补上稳定的 /posts/... 后重启开发服务器;不要继续沿用随机地址 |
| 侧边栏没有文章 | sidebar.ts 扫描 | 文件是否在 docs/posts、是否叫 index.md、sidebar 是否误写 false、是否重启 |
| 目录页没有文章 | catalogue | layout: page、catalogue: true、path 是否精确指向真实 posts 目录、目录页是否需要重启扫描 |
| 首页文章流没有 | Teek 文章数据 | article: true、inHomePost 不是 false、frontmatter 分隔线完整、date 合法 |
| 分类页没有 | categories | categories 是否写成数组、名称是否和预期一致、article 是否为 true |
| 标签页没有 | tags | 标签拼写、大小写和数组格式是否一致、文章是否被 article: false 排除 |
| 精选卡片没有 | top 配置 | 是否写 top: 1,首页 topArticle.enabled 是否开启且 limit 允许 |
| 置顶顺序不对 | sticky/date | sticky 是否为正整数、同值文章是否按日期排序;不要用 sidebarSort 调整首页 |
| 搜索结果找不到 | Pagefind | pagefind-indexed: true、是否重新构建、构建日志是否有 Pagefind 报错、是否在预览环境测试 |
| 搜索分组/顺序不对 | search-manifest | 文件是否在 posts、article 是否 false、是否重启扫描、sidebarSort 和数字前缀是否正确 |
| 评论区没有 | comment | comment: true、评论 provider 配置和外部服务是否正常 |
| 封面不是随机图或一直固定 | coverImg / 正文图片 | 如果写了 coverImg 会优先固定显示;删除它才会走正文图片或 /home/bg*.webp 随机。图片路径必须真实存在 |
| 图片 404 | public 资源路径 | 文件是否在 docs/public/images/posts/<slug>/,引用是否以 /images/ 开头,大小写是否一致 |
| RSS 少文章 | RSS 过滤 | article 是否为 false、文件路径是否含「写作规范」、是否重新构建 |
| sitemap 少页面 | noindex 或路由 | 页面是否命中 noindexPaths、permalink 是否生成、是否重新构建 |
| 分类顺序不对 | categoryOrder | 是否同时改了 sidebar.ts 和 search-manifest.ts,顺序是否在数组里,是否重启 |
| 导航没有新入口 | Nav.ts | 顶部导航是手写的,必须编辑 docs/.vitepress/ConfigHyde/Nav.ts |
| 首页卡片没有新入口 | Features.ts | 首页 Banner 卡片是手写的,必须编辑 docs/.vitepress/ConfigHyde/Features.ts |
| 改了 dist 没效果 | 生成了错误文件 | 不要改 dist;回源文件构建,pnpm docs:build 会重新生成 |
| 审计报 H1 数量错误 | 页面结构 | 页面只能有一个 H1;检查是否在正文又复制了一个 # 标题 |
| 审计报图片缺 alt | 图片 Markdown | 给所有  写 alt 属性;描述性图片写清内容,纯装饰图片可以写空 alt(alt=""),但不要省略属性 |
排查顺序建议固定为:先看终端错误,再确认源文件路径,再确认 frontmatter,再看开发服务器是否需要重启,最后才怀疑构建或部署。 绝大多数「页面没出现」都是路径、开关或缓存时序问题,而不是主题坏了。
如果你已经能定位到具体症状,继续阅读构建、发布与故障排查;如果问题在目录入口本身,转到目录页完整流程。

