构建、预览、审计与发布完整流程
读者对象:已经写好 Markdown,但还不知道如何把文章安全发布到本站的初学者。
本页按真实顺序讲清楚:本地开发、生产构建、SEO 审计、构建产物预览、Git 提交、CNB 自动构建、EdgeOne 部署、线上验收、缓存处理和回滚。
所有命令默认在项目根目录
teek-hyde中执行。不要在其他目录执行,也不要把命令前面的$或PS>一起复制。
如果还不熟悉正文写法,先阅读 Markdown 写作基础与本站扩展。
发布流程总览
发布不是「保存 Markdown 文件」这么简单。本站使用 VitePress 生成静态站点,再由 CNB 和 EdgeOne 自动部署,完整链路是:
编写 Markdown
→ pnpm docs:dev 本地开发与检查
→ pnpm docs:build 生产构建
→ pnpm docs:audit 检查 dist 的 SEO 基线
→ pnpm docs:preview 预览生产构建
→ git add / git commit / git push origin main
→ CNB 自动安装依赖、重新构建、再次审计
→ EdgeOne Makers 发布 docs/.vitepress/dist
→ 在正式域名上验收最重要的是顺序:
- 先在开发服务器里把内容和交互检查好;
- 再构建生产文件;
- 构建成功后才运行审计;
- 审计成功后预览构建产物;
- 最后才提交并推送;
- 推送后等待 CNB 和 EdgeOne 完成部署;
- 到线上做最终验收。
先认识这些术语
| 术语 | 通俗解释 |
|---|---|
| Markdown | 用 #、列表、代码块等纯文本语法写内容的格式 |
| VitePress | 把 Markdown 构建成静态网站的框架 |
| Teek / Teek-Hyde | 本站使用的 VitePress 主题和定制层,决定页面布局、侧边栏、卡片外观等 |
| 开发服务器 | 只在本机运行的服务,用来边改边看,默认端口 5174 |
| HMR | Hot Module Replacement,热更新;保存文件后页面局部自动更新 |
| 生产构建 | 把源码编译成可部署的 HTML、CSS、JavaScript 和静态资源 |
| dist | 构建产物目录,本项目是 docs/.vitepress/dist |
| 预览服务器 | 不是开发服务器,只负责把 dist 当作静态网站提供服务,默认端口 4173 |
| Pagefind | 本站使用的离线全文搜索索引生成器 |
| SEO | 搜索引擎优化,包括标题、描述、canonical、sitemap 等 |
| canonical | 告诉搜索引擎某个页面的正式 URL,避免重复收录 |
| sitemap | 给搜索引擎的站点 URL 清单,本项目生成 sitemap.xml |
| RSS | 文章订阅源,本项目生成 feed.rss |
| 锁文件 | pnpm-lock.yaml,记录依赖的精确版本,保证不同机器安装结果一致 |
| CI/CD | 持续集成/持续部署,本项目使用 CNB 自动构建和部署 |
| EdgeOne | 腾讯云边缘安全加速平台,本项目用 EdgeOne Makers 托管静态站点 |
| CDN 缓存 | 边缘节点保存的旧网页副本;部署后有时需要刷新 |
为什么本地开发不能直接当发布
pnpm docs:dev 生成的是开发服务器内容,包含热更新和调试逻辑,不会生成完整的 docs/.vitepress/dist。线上需要的是生产构建和 Pagefind 索引,不是你的本地开发端口。
因此:
- 本地能打开页面,只能证明内容大致可用;
- 线上发布前必须执行
pnpm docs:build; - SEO 审计读取的是构建后的 dist;
- 预览服务器读取的也是 dist;
- 推送后 CNB 还会从源码重新构建一次。
准备开发环境
第 1 步:检查 Node.js
本站依赖 Vite 7。当前项目安装的 Vite 版本要求 Node.js 满足:
^20.19.0 || >=22.12.0推荐使用 Node.js 22 LTS,并满足 >=22.12.0。CNB 发布流水线也使用 Node 22 镜像。
在终端执行:
node -v预期看到类似:
v22.12.0只要满足上面的版本范围即可。若版本太低,先升级 Node.js,再继续。Node.js 是运行 VitePress、Pagefind、构建脚本和 pnpm 的基础环境。
第 2 步:检查 pnpm
项目使用 pnpm,并通过 pnpm-lock.yaml 锁定依赖。执行:
pnpm -v预期看到类似:
10.x.x只要能正常运行 pnpm 即可。没有安装时,可以按官方方式安装,CNB 中使用的是:
npm install -g pnpm不要在项目中使用 npm install 安装项目依赖,也不要混用 npm、yarn 和 pnpm。混用会生成不同结构的 node_modules,可能导致脚本失败或 lockfile 变化。
第 3 步:打开或克隆仓库
已经拿到项目目录时:
cd E:\vitepress-teek-my-blog\teek-hyde路径只是示例,请换成你的实际路径。
还没有仓库时:
git clone <仓库地址> teek-hyde
cd teek-hyde尖括号是占位符,必须替换成真实仓库地址,不要把尖括号原样执行。
进入目录后确认当前位置:
Get-LocationGit Bash、macOS 或 Linux 可以使用:
pwd预期看到仓库根目录,里面应有 docs/、package.json、pnpm-lock.yaml 和 .cnb.yml。
第 4 步:安装依赖
首次拿到项目时:
pnpm install安装完成后应看到类似 Done in ... 的提示,并出现 node_modules/。
如果只是按锁文件还原一套完全一致的依赖:
pnpm install --frozen-lockfile两者区别:
| 命令 | 作用 | 何时使用 |
|---|---|---|
pnpm install | 根据 package.json 和 lockfile 安装;锁文件需要时会更新 | 首次开发、确认要调整依赖时 |
pnpm install --frozen-lockfile | 严格按现有 lockfile 安装,不修改 lockfile;两者不一致就失败 | CI、干净环境、普通写文章时 |
本项目只是写文章时,不应该随意修改依赖。--frozen-lockfile 失败通常说明 package.json 与 pnpm-lock.yaml 不一致,不要用删除 lockfile 的办法掩盖问题。
node_modules/、dist/、缓存等生成目录不应提交到 Git。
环境准备常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
pnpm: command not found | 没安装 pnpm 或 PATH 未生效 | 重新打开终端,检查 pnpm -v |
ERR_PNPM_UNSUPPORTED_ENGINE 或 Vite 启动失败 | Node.js 版本过低 | 升级到 Node 22 LTS 并重新打开终端 |
| 安装过程修改了 lockfile | 使用了 npm/yarn,或依赖声明与锁文件不一致 | 确认使用 pnpm;不要提交无意产生的 lockfile 变化 |
--frozen-lockfile 失败 | 依赖清单与锁文件不同步 | 先理解原因;不要随意删除 lockfile 或强行绕过 |
四个核心命令
项目 package.json 定义了四个命令:
| 命令 | 实际执行内容 | 端口 | 作用 | 什么时候用 |
|---|---|---|---|---|
pnpm docs:dev | vitepress dev docs | 默认 5174 | 启动开发服务器,支持 HMR | 写文章、调样式、即时检查 |
pnpm docs:build | 以 4GB 内存上限运行 VitePress 构建 | 无 | 生成 docs/.vitepress/dist | 审计、预览和发布前 |
pnpm docs:preview | vitepress preview docs | 默认 4173 | 静态预览构建产物 | 检查 dist 的真实效果 |
pnpm docs:audit | node scripts/audit-site.mjs | 无 | 检查构建后的 SEO 与页面结构 | 必须在本机构建后 |
开发端口来自 docs/.vitepress/config.ts:
vite: {
server: {
host: "0.0.0.0",
port: 5174,
strictPort: false,
},
}strictPort: false 表示端口被占用时 Vite 可能自动尝试下一个端口。因此始终以终端打印的 Local: 地址为准,不要盲目认为一定是 5174。
预览端口 4173 是 VitePress 预览服务器的默认端口。可以用参数改成 4174,后文会给出完整命令。
为什么有四个命令
docs:dev速度快,但不等于生产结果;docs:build才生成可部署文件;docs:audit依赖构建产物,不能替代构建;docs:preview用来检查构建产物,不会重新编译源码。
四个命令不是互斥的四个网站,而是同一条发布链路的不同阶段。
本地开发:边写边看
第 1 步:确认在项目根目录
Get-Location或:
pwd如果当前目录不是 teek-hyde,先 cd 回来。命令在错误目录执行时,常见表现是找不到 package.json 或 docs。
第 2 步:启动开发服务器
pnpm docs:dev预期终端出现类似:
VITE v7.x.x ready in ... ms
➜ Local: http://localhost:5174/
➜ Network: http://192.168.x.x:5174/如果 5174 被占用,实际端口可能不是 5174,以 Local: 为准。
第 3 步:在浏览器打开页面
打开:
http://localhost:5174/还可以直接打开写作相关内容:
http://localhost:5174/writing-guide/markdown-basics
http://localhost:5174/writing-guide/publishing这里使用的是页面 frontmatter 中的 permalink,不带 .html。
第 4 步:验证 HMR
HMR 是热更新:修改已有 Markdown 的正文并保存后,浏览器页面通常会自动更新,不必每次都重启服务器。新增、删除或移动文章文件不属于普通正文热更新,见下一步。
可以这样测试:
- 打开一篇正在编辑的文章;
- 把某段正文改一个词;
- 保存文件;
- 观察浏览器是否自动更新;
- 再看终端是否出现重新编译提示。
如果只是普通正文修改,通常可以立即看到。如果页面没有更新,先看终端是否报错,再尝试普通刷新。
第 5 步:知道哪些修改需要重启
以下变更通常不能只靠 HMR,需要停止再启动开发服务器:
docs/.vitepress/config.ts、plugins.ts、site.ts等配置;docs/.vitepress/sidebar.ts生成逻辑;docs/.vitepress/search-manifest.ts生成逻辑;- 新增、删除或修改
catalogue: true的目录页数据; - 新增、删除或移动文章文件;
- 新增文章目录或大幅调整文章目录结构;
- 插件、依赖、主题入口发生变化。
原因是这些数据往往在 Vite 启动或配置加载时扫描生成,不是单个 Markdown 文件的热更新范围。
第 6 步:停止服务器
在运行开发服务器的终端按:
Ctrl+C看到命令提示符重新出现,说明服务器已停止。不要直接关闭终端窗口后以为端口一定释放;有时后台进程还占用端口,后文故障排查会说明。
本地开发常见错误
| 错误 | 为什么会出问题 | 正确做法 |
|---|---|---|
用 file:///.../index.html 打开 | 没有经过 VitePress 服务器 | 使用 http://localhost:端口/ |
| 端口写死成 5174 | 端口被占用时 Vite 可能换了端口 | 看终端 Local: 地址 |
修改 dist 里的文件 | dist 是构建产物,会被覆盖 | 修改 docs/ 下的源码 |
| 新增目录后只刷新浏览器 | 目录扫描数据没有重启生成 | 停止后重新 pnpm docs:dev |
| 在错误目录执行 pnpm | 找不到脚本或 docs | 先进入仓库根目录 |
生产构建:生成可部署文件
第 1 步:保存所有修改
构建读取磁盘上的文件。编辑器里没保存的内容不会被构建,因此先保存所有 Markdown、配置和图片。
第 2 步:执行生产构建
在项目根目录运行:
pnpm docs:build这个脚本实际使用:
node --max-old-space-size=4096 node_modules/vitepress/bin/vitepress.js build docs--max-old-space-size=4096 给 Node.js 约 4GB 内存上限。首次构建会比 HMR 慢,因为它要生成完整页面、压缩资源、生成搜索索引、RSS 和 sitemap。
预期成功标志:
- 进程正常结束并回到命令提示符;
- 终端出现 VitePress 构建完成提示;
- 终端出现 Pagefind 索引生成相关日志;
- 看到
.gz压缩输出也属正常; docs/.vitepress/dist/存在且包含页面、资源、pagefind/、feed.rss、sitemap.xml等文件。
如果只看到构建日志但最后有错误并以非零状态退出,就不是成功构建。不要继续审计或发布。
第 3 步:检查构建产物目录
PowerShell:
Get-ChildItem docs\.vitepress\distGit Bash、macOS 或 Linux:
ls docs/.vitepress/dist会看到类似:
404.html
index.html
posts/
catalog/
writing-guide/
pagefind/
feed.rss
sitemap.xml
assets/不同内容和插件版本可能导致具体文件名略有变化,这是正常的。
第 4 步:认识构建自动生成的文件
| 产物 | 位置 | 作用 | 是否手工编辑 |
|---|---|---|---|
| HTML 页面 | docs/.vitepress/dist/**/*.html | 最终网页 | 否 |
| 静态资源 | docs/.vitepress/dist/assets/、home/、images/ 等 | CSS、JS、图片等 | 否 |
| Pagefind 搜索索引 | docs/.vitepress/dist/pagefind/ | 站内全文搜索 | 否 |
| RSS 订阅源 | docs/.vitepress/dist/feed.rss | 文章订阅 | 否 |
| sitemap | docs/.vitepress/dist/sitemap.xml | 给搜索引擎的 URL 清单 | 否 |
| 搜索清单副本 | docs/.vitepress/dist/search-manifest.json | 从源文件生成并复制进 dist | 否 |
| gzip 压缩文件 | 带 .gz 的资源文件 | 由压缩插件生成 | 否 |
这些文件的源头分别是 Markdown、图片、VitePress 配置和插件配置。修改 dist 只是临时改一个会被下一次构建覆盖的副本,绝不能把它当成修复源码的方法。
第 5 步:理解 Pagefind、RSS 和 sitemap
Pagefind 搜索
docs/.vitepress/plugins.ts 中配置了 Pagefind。它在构建阶段扫描页面并生成 dist/pagefind/ 索引;普通文章需要:
pagefind-indexed: true写作指南页面当前使用 pagefind-indexed: false,所以不会进入搜索。这不是 bug,是页面类型的预期行为。
RSS
RSS 插件会生成:
docs/.vitepress/dist/feed.rss线上地址是:
https://www.baecon.cn/feed.rssRSS 主要收录普通文章,不收录 article: false 的指南、目录和工具页。
sitemap
docs/.vitepress/config.ts 配置了 sitemap.hostname,构建后生成:
docs/.vitepress/dist/sitemap.xml线上地址是:
https://www.baecon.cn/sitemap.xml配置会过滤掉指定的 noindex 工具页,例如搜索页、留言页、友链页、导航页和风险链接页。不要手工编辑 sitemap.xml。
第 6 步:不要修改 dist
docs/.vitepress/dist/ 是构建输出,不是源码:
- 下次
pnpm docs:build会覆盖或重新生成; - dist 通常被
.gitignore忽略; - CNB 部署的是它自己重新构建出来的 dist;
- 你手工修复 dist 不会进入源码,也不会被后续部署保留。
需要改页面就改 docs/ 下的源文件;需要改图片就改 docs/public/ 下的源图片。
生产构建常见错误
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 编译中途退出 | frontmatter、Markdown 或代码语法错误 | 查看第一条错误和出错文件 |
| Pagefind 没有生成 | 构建未真正完成,或搜索插件报错 | 重新构建并查看完整日志 |
heap out of memory | 构建进程内存不足 | 关闭占内存程序后重试;不要随意删构建脚本里的内存参数 |
| 构建成功但页面空白 | 模板、组件或配置问题 | 用 pnpm docs:preview 检查,再看浏览器控制台 |
| 修改 dist 后线上没变化 | 改的是构建产物,不是源码 | 改源文件并重新构建、提交、推送 |
上线前 SEO 审计
第 1 步:确认已经构建
审计脚本 scripts/audit-site.mjs 读取:
docs/.vitepress/dist/所以必须先:
pnpm docs:build再:
pnpm docs:audit如果 dist 不存在或还是旧版本,审计要么读取失败,要么检查旧页面。审计不能代替构建。
第 2 步:运行审计
pnpm docs:audit本仓库当前一次实际输出是(页面数会随内容变化):
SEO 审计:检查 34 个 HTML 页面,sitemap 29 个 URL
结果:未发现阻塞性 SEO 错误。34 是除 404.html 外参与检查的 HTML 页面数;29 是 sitemap 中的 URL 数。两个数字不是固定的质量指标,新增文章、目录页、指南页,或配置 noindex 页面,都会让数字变化。
写作指南扩充后,下一次构建可能得到不同页面数;不要为了追求固定的 34/29 去增删页面。
第 3 步:正确解读结果
审计结果分两类:
| 类型 | 是否阻塞发布 | 含义 |
|---|---|---|
错误: | 是 | 必须修复,否则 CNB 不应继续部署 |
警告: | 通常不阻塞退出码,但仍需判断 | 例如可收录页面没有出现在 sitemap |
真正决定能否通过的是:脚本是否以 0 退出,以及有没有 错误:。只有 结果:未发现阻塞性 SEO 错误。 才表示没有阻塞项。
如果出现警告,也不要直接忽略。先判断是页面类型、noindex 配置还是 sitemap 过滤造成,再决定是否需要修改。
第 4 步:审计通常检查什么
当前脚本会读取每个 HTML 文件并检查:
<title>是否存在;- description 是否存在;
- canonical 是否存在且指向正式域名;
- robots 是否存在;
- Open Graph 的 title 和 description 是否存在;
- 非 noindex 页面是否恰好有一个 H1;
<img>是否带有alt属性;- 可收录页面是否出现在 sitemap;
- noindex 页面是否错误地出现在 sitemap。
其中任何阻塞错误都会让审计失败。注意:ignoreDeadLinks=true 使构建不拦截失效链接;审计不检查图片文件是否存在,空 alt 也可能通过;链接、图片和摘要质量必须人工检查。审计是页面结构检查,不代替人工检查内容是否有错别字、链接是否有意义或手机端是否易读。
第 5 步:出现审计错误时怎么修
按以下顺序处理:
- 阅读错误行,找到第一个报错页面;
- 看错误是 title、description、canonical、H1、alt 还是 sitemap;
- 修改对应 Markdown 的 frontmatter 或正文;
- 重新
pnpm docs:build; - 再运行
pnpm docs:audit; - 重复直到没有阻塞错误。
常见对应关系:
| 审计提示 | 最可能的原因 | 修复方向 |
|---|---|---|
人工发现没有 title | 站点标题兜底可能让审计通过 | 补 frontmatter title,不要只依赖审计 |
| 人工发现没有摘要 | 站点描述兜底可能让审计通过 | 补 frontmatter description |
| h1 数量为 0 | 正文没有 H1 | 添加唯一 H1 |
| h1 数量为 2 | 正文有两个 H1 | 删除一个,改成 H2 |
| 缺少 canonical | 页面头信息生成异常 | 检查 permalink 和站点配置,不要手改 dist |
| canonical 不是正式域名 | URL 使用了错误域名或构造异常 | 检查 SITE.url、permalink 和配置 |
| 图片缺少 alt 属性 | Markdown 括号为空或 HTML 图片漏 alt | 补有意义 alt;空 alt 仍可能通过审计 |
| noindex 页面出现在 sitemap | 工具页过滤与页面配置不一致 | 修正页面类型或由维护者调整过滤配置 |
审计常见错误
- 还没 build 就 audit:先构建;
- 构建失败后继续 audit:先解决构建错误;
- 只修改 dist:下次构建就丢;
- 看到警告就改 sitemap 文件:sitemap 是生成文件;
- 为了让数字变少而删除正常页面:数字不是目标,结构正确才是目标。
预览生产构建
第 1 步:先完成构建
pnpm docs:build预览服务器不会替你编译 Markdown。没有 dist 或 dist 很旧,预览到的就是旧内容。
第 2 步:启动预览服务器
pnpm docs:preview预期终端出现类似:
➜ Local: http://localhost:4173/打开这个地址,检查真实构建效果。
第 3 步:指定其他预览端口
如果 4173 被占用,可以使用:
pnpm docs:preview -- --port 4174然后打开:
http://localhost:4174/这里的双横线 -- 表示把后面的参数继续传给 VitePress。不要写成 pnpm docs:preview --port 4174 后期待一定能生效。
第 4 步:理解开发服务器和预览服务器的区别
| 项目 | pnpm docs:dev | pnpm docs:preview |
|---|---|---|
| 使用源码还是 dist | 源码,按需编译 | 已生成的 dist |
| 默认端口 | 5174 | 4173 |
| HMR | 有 | 没有 |
| 搜索索引 | 开发阶段可能不完整 | 使用构建生成的 Pagefind |
| 适合检查什么 | 写作、布局、交互 | 生产构建、链接、搜索、SEO 结果 |
| 修改后是否自动更新 | 通常自动 | 不会重新构建;需要重新 build 并重启预览 |
预览服务器看到的效果更接近线上,但它仍然不是 EdgeOne。域名、CDN、统计、评论网络等仍要在正式站点验证。
第 5 步:重新构建后重启预览
如果你修改了 Markdown 或配置,正确顺序是:
- 在预览终端按
Ctrl+C停止; - 运行
pnpm docs:build; - 再运行
pnpm docs:preview; - 浏览器刷新并检查新产物。
为什么必须重启?预览服务器面向固定的 dist 和带哈希的资源文件。重新构建后,HTML 可能引用新的资源文件名;继续用旧预览进程或旧页面缓存,容易出现样式丢失、搜索索引过期或看到旧页面。重启是最稳妥的做法。
预览常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
| 打开 4173 不是最新文章 | 没有重新构建或预览进程还在用旧 dist | build 后重启 preview |
| 搜索还是旧结果 | Pagefind 索引没有更新 | 重新构建,重启预览,硬刷新 |
| 页面没有 HMR | 预览服务器本来就不支持 HMR | 开发用 docs:dev,验收用 docs:preview |
| 样式和开发模式不同 | 看到的是生产构建 | 这是正常的,按生产结果检查 |
提交前检查 Git 变更
Git 是版本控制工具。提交前要确认:只提交文章、图片和必要的源码,不提交构建产物、缓存和临时文件。
第 1 步:查看当前改动
git status --short常见标记:
| 标记 | 含义 |
|---|---|
M | 已修改的已跟踪文件 |
A | 已暂存的新文件 |
D | 已删除的已跟踪文件 |
?? | 尚未被 Git 跟踪的新文件 |
新手最容易漏掉的是尚未跟踪的图片:git diff --stat 不会显示未跟踪文件,必须看 git status --short。
第 2 步:查看改动规模
git diff --stat它会显示已跟踪文件改了多少行。新文件不会出现在这里,所以还要看 git status --short。
检查具体文本差异:
git diff -- docs/posts/技术笔记/01.example-article.md如果文件是全新的,git diff 在暂存前可能没有输出,可以直接在编辑器中复查,或暂存后再看:
git add docs/posts/技术笔记/01.example-article.md
git diff --cached -- docs/posts/技术笔记/01.example-article.md第 3 步:检查新增图片
先看状态:
git status --short docs/public/images/posts再列出某个文章目录:
Get-ChildItem docs\public\images\posts\example-article -FileGit Bash、macOS 或 Linux:
ls docs/public/images/posts/example-article确认:
- 图片确实存在;
- 文件名与 Markdown 中引用完全一致;
- 没有把本地缩略图、临时截图和重复文件一起带上;
- 没有误放
C:\Users\...这样的本地路径; - 每张图片都有用途和 alt。
第 4 步:确认不要提交的生成文件
| 路径 | 来源 | 为什么不要提交 |
|---|---|---|
docs/.vitepress/dist/ | pnpm docs:build | 构建产物,CNB 会重新生成 |
docs/.vitepress/cache/ | VitePress/Vite 缓存 | 本机缓存,体积大且环境相关 |
docs/public/search-manifest.json | plugins.ts 启动时由文章目录生成 | 每次会重新生成,手改没有意义 |
stats.html | 可选的构建分析报告 | 分析产物,不是站点源码 |
node_modules/ | 依赖安装 | 可由 lockfile 重建,体积巨大 |
*.log | 本地日志 | 临时文件,可能含本机信息 |
.gitignore 已经忽略这些路径,但不要用 git add -f 强制加入。如果不确定某个文件是否被忽略:
git check-ignore -v docs/.vitepress/dist/index.html输出说明匹配了哪条忽略规则;没有输出通常表示没有被忽略。
第 5 步:暂存正确的文件
只添加本次发布需要的源码,例如:
git add docs/posts/技术笔记/01.example-article.md
git add docs/public/images/posts/example-article
git add docs/writing-guide/markdown-basics.md
git add docs/writing-guide/publishing.md如果改了导航或目录页,再按需添加:
git add docs/catalog/技术笔记.md
git add docs/.vitepress/ConfigHyde/Nav.ts不要为了省事直接:
git add .除非你已经逐项检查过工作区,否则它可能把无关改动、临时文件或不应提交的图片一起加入。
第 6 步:复查暂存区
git diff --cached --stat
git status --short预期只看到本次文章相关文件,状态中不应出现 dist、cache、search-manifest 或 stats.html。
Git 检查常见错误
- 漏提交图片:页面线上 404;
- 把 dist 一起提交:仓库变大且容易冲突;
- 把
search-manifest.json当源码修改:下次启动就被覆盖; - 用
git add .带上了无关文件:发布范围失控; - 提交了包含 Token 的
.env或日志:立即停止推送并按安全流程处理。
提交与推送
第 1 步:确认当前分支
git branch --show-current预期:
mainCNB 的 .cnb.yml 只在推送到 main 时触发发布。如果当前不是 main,不要假设推送后一定自动部署;先确认团队的分支流程。
第 2 步:创建提交
一条清楚的提交信息应该说明改了什么。例如:
git commit -m "docs: add markdown writing guide"或者:
git commit -m "docs: 新增 Markdown 写作基础指南"预期看到类似:
[main abc1234] docs: add markdown writing guide
3 files changed, 600 insertions(+)提交信息不是越长越好,但要能看出这次发布的内容。不要写 update、test、修改 这类无法判断范围的标题。
第 3 步:推送 main 分支
git push origin main预期看到类似:
To <仓库地址>
abc1234..def5678 main -> main推送成功只表示代码到了远端仓库,不表示线上已经更新。线上更新要等 CNB 构建和 EdgeOne 部署成功。
第 4 步:确认推送后发生了什么
推送 main 后,CNB 会自动:
- 安装依赖;
- 重新
docs:build; - 运行
docs:audit; - 部署 dist 到 EdgeOne。
如果本地能构建但 CNB 失败,常见原因是 lockfile、Node 版本、敏感变量缺失,或只在你本机存在但没有提交的文件。
推送常见错误
| 现象 | 原因 | 处理 |
|---|---|---|
nothing to commit | 没有暂存改动,或已经提交过 | 检查 git status |
| 推送被拒绝 | 远端 main 有新提交 | 先同步远端;不要直接强推 |
| 页面在本地有、线上没有 | 文件没提交、图片没提交、CNB 失败或缓存未更新 | 从提交记录和 CNB 日志逐项检查 |
| 推送后没有触发 CNB | 推到了非 main 分支 | 确认目标分支是 main |
| 提交里出现 Token 或密码 | 敏感信息被误加入版本库 | 立即停止,按项目安全流程处理,不要继续公开 |
如果推送被拒绝,初学者最安全的做法是先询问维护者。熟悉 Git 的人可以按团队约定同步远端后重新提交,但不要使用 git push --force 覆盖别人的 main 分支。
CNB 自动构建与部署
触发器:只有 main 的 push
.cnb.yml 的顶层结构是:
main:
push:这表示只有向 main 分支执行 push 时才进入这条流水线。其他分支推送、单纯的本地保存、创建文件但没有提交,都不会自动部署。
流水线运行环境
.cnb.yml 中声明的发布环境包括:
- runner:16 核 CPU;
- services:
docker和git-clone-yyds; - Docker 镜像:
docker.cnb.cool/znb/images/node:22; - 构建缓存:
/data/.cache:copy-on-write; - 部署阶段使用
node:22镜像; - 环境变量通过 imports 从 CNB 的 secrets 仓库引入。
不要在文章、截图、日志或提交信息里暴露 secrets 内容。真实 Token 只应存在于受控的 CNB 环境变量中。
流水线阶段顺序
.cnb.yml 中的实际阶段按顺序如下:
| 顺序 | 阶段 | 关键脚本或动作 | 作用 |
|---|---|---|---|
| 1 | 构建当前项目 | 写入构建时间环境变量 | 记录本次构建信息 |
| 2 | 设置淘宝源 | npm config set registry https://registry.npmmirror.com | 使用国内镜像加速安装 |
| 3 | 打印环境 | npm install -g pnpm@12.16.1 && node -v && npm -v && yarn -v && pnpm -v | 固定 pnpm 版本并输出环境版本 |
| 4 | 安装依赖 | pnpm install --frozen-lockfile | 严格按 lockfile 安装 |
| 5 | 编译项目 | pnpm docs:build | 生成生产构建和搜索/RSS/sitemap |
| 6 | SEO Audit | pnpm docs:audit | 检查 SEO,错误会阻止后续部署 |
| 7 | 部署到 EdgeOne Makers | npx --yes edgeone@1.6.47 makers deploy ./docs/.vitepress/dist -n my-blog-teek -e production -t $EDGEONE_PAGES_API_TOKEN | 使用固定 EdgeOne CLI 版本上传 dist |
| 8 | 构建知识库 | 扫描 docs/**/*.md 和 docs/**/*.txt | CNB 的额外知识库任务,不是网页发布本身 |
核心发布命令可以单独理解为:
pnpm install --frozen-lockfile
pnpm docs:build
pnpm docs:audit
npx --yes edgeone@1.6.47 makers deploy ./docs/.vitepress/dist -n my-blog-teek -e production -t $EDGEONE_PAGES_API_TOKEN为什么审计在部署前
顺序是 build → audit → deploy:
- build 生成 dist;
- audit 读取 dist 并检查 SEO;
- audit 通过后,才部署 dist。
如果 audit 以错误状态退出,正常的 CI 流程不会继续执行后面的部署步骤。这样能避免把 H1 数量错误、缺少 alt 属性、canonical 或 sitemap 不一致的页面发布出去;它不能替代人工检查摘要、链接、图片文件和空 alt。
不要暴露 Token
部署命令中的:
$EDGEONE_PAGES_API_TOKEN表示从 CNB 环境中读取变量。不要:
- 把真实 Token 写进
.cnb.yml; - 把 Token 写进 Markdown、README 或代码注释;
- 在终端执行
echo $EDGEONE_PAGES_API_TOKEN并截图; - 把包含环境变量的完整构建日志公开发布;
- 在本地创建并提交存放 Token 的文件。
Token 缺失或过期时,部署阶段会失败。正确做法是由有权限的维护者在 CNB 的受控设置中更新或轮换密钥,而不是修改文章内容来绕过。
CNB 常见错误
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 流水线没有启动 | 不是 push 到 main | 确认分支和推送成功 |
ERR_PNPM_OUTDATED_LOCKFILE | package.json 与 lockfile 不一致 | 恢复正确 lockfile,或由维护者明确处理依赖变更 |
| build 阶段失败 | 源码、frontmatter、依赖或内存问题 | 先在本机复现并修复 |
| audit 阶段失败 | 存在阻塞性 SEO 错误 | 按审计错误修复后重新构建并推送 |
| 部署阶段失败 | Token 缺失/过期、EdgeOne 权限或网络问题 | 联系有权限的维护者检查 secrets 和部署配置 |
| 知识库阶段失败 | 知识库扫描或服务问题 | 检查 CNB 日志;它通常在部署之后,不能据此判断网页是否已部署 |
部署成功的判断标准
看到 CNB 全部核心阶段成功,并且 EdgeOne 显示新的部署版本或部署完成,才可以进入线上验收。仅仅看到 git push 成功不算发布完成。
上线后验收
线上验收必须打开正式域名,不要用 localhost:
https://www.baecon.cn/第 1 步:确认部署版本
- 打开 CNB 本次构建记录;
- 确认
安装依赖、编译项目、SEO Audit、部署到 EdgeOne Makers都成功; - 打开 EdgeOne 控制台,确认最新部署版本存在;
- 用无痕窗口打开首页,避免本地缓存干扰。
第 2 步:检查首页文章卡片
打开:
https://www.baecon.cn/检查新文章是否出现在预期位置:
- 标题正确;
- description 摘要合理;
- 封面图显示,不失真或 404;
- 日期、分类、标签正确;
- 点击卡片能进入文章详情;
- 精选文章只有在设置
top: 1等标记后才进入精选区,不要把所有普通文章都当作精选。
第 3 步:检查文章详情
打开新文章的 permalink,例如:
https://www.baecon.cn/posts/技术笔记/example-article逐项检查:
- 地址不带
.html; - 页面只有一个 H1;
- H2/H3 层级和右侧目录正常;
- 正文、图片、alt、图注正常;
- 代码块有语言标签、行号、复制按钮;
- 代码组和时间线没有错位;
- 文章信息区显示日期、分类、标签、字数、阅读时长和更新时间;
- 浏览量等 Umami 数据只在正式域名上产生,不要在本地判断线上统计。
第 4 步:检查目录页和侧边栏
打开顶部导航中的目录入口,例如:
https://www.baecon.cn/catalog/技术笔记检查:
- 新文章出现在目录页;
- 目录页不显示左侧栏、评论,也不进入搜索;
- 文章详情页的左侧栏能找到新文章;
- 点击侧边栏项目能正确跳转;
- 修改或新增目录页后,如果本地开发环境没有刷新,先重启开发服务器。
第 5 步:检查搜索
在正式站点点击搜索,输入新文章标题中的独特词或正文中的独特短语。
检查:
- 能搜到
pagefind-indexed: true的新文章; - 搜索结果标题和链接正确;
- 点击结果能进入正确 route;
- 写作指南、目录页和工具页按当前配置不进入搜索是预期行为;
- 如果搜不到,先重新
pnpm docs:build,再重启预览并硬刷新。
第 6 步:检查评论
打开新文章底部评论区:
- 文章应设置
comment: true; - 目录页和写作指南使用
comment: false,不显示评论是预期行为; - 评论框能加载,提交测试评论前先确认不会污染正式讨论;
- 如果评论加载失败,打开浏览器开发者工具的 Network 面板,检查 Twikoo 外部请求是否被拦截;
- 评论组件依赖外部 Twikoo 服务,网络或第三方服务异常不是 Markdown 构建错误。
评论测试不要提交垃圾内容。测试完成后按实际权限清理。
第 7 步:检查分类、标签和归档
打开:
https://www.baecon.cn/categories
https://www.baecon.cn/tags
https://www.baecon.cn/archives检查:
- 新文章进入正确的分类;
- 标签列表包含文章的标签;
- 日期归档正确;
- 从这些页面点击进入文章时链接有效;
- 没有重复文章或错误分类;
- 目录页和写作指南不进入这些文章聚合页,这是按文章类型区分的预期行为。
第 8 步:检查 RSS 和 sitemap
打开:
https://www.baecon.cn/feed.rss
https://www.baecon.cn/sitemap.xml检查:
feed.rss能打开并包含新文章;- RSS 中的链接是正式域名和正确 permalink;
sitemap.xml包含新文章;- sitemap 不包含配置过滤掉的无索引工具页;
- 文件内容不是本地 dist 里的旧时间或旧 URL。
第 9 步:检查 canonical
在文章页按 Ctrl+U 查看源代码,或打开开发者工具的 Elements 面板,搜索:
<link rel="canonical" href="...">源码中的中文路径通常会被百分号编码,浏览器地址栏和搜索引擎会解码显示。不要因为看到 %E6%8A%80... 就误判为错误。下面是解码后的可读形式:
<link rel="canonical" href="https://www.baecon.cn/posts/技术笔记/example-article">检查:
- 使用
https://www.baecon.cn正式域名; - 使用 permalink 对应的路径;
- 没有
.html; - 没有 localhost;
- 没有重复的 canonical;
- 分享页、搜索页等工具页的 noindex 设置符合配置。
第 10 步:检查移动端
使用浏览器开发者工具的设备模拟,或直接用手机访问正式站点,检查:
- 文章正文没有横向滚动;
- 代码块可以水平和垂直滚动;
- 表格没有把整个页面撑破;
- 图片自适应宽度;
- 侧边栏和导航可以打开、关闭;
- 搜索框可用;
- 评论区不遮挡内容;
- 长标题不会溢出。
第 11 步:检查文章元数据
检查页面 H1 下方或文章底部区域:
- 标题;
- 创建日期;
- 更新时间;
- 分类;
- 标签;
- 字数;
- 阅读时长;
- 浏览量;
- 分享按钮。
「最后更新于」来自构建系统的更新时间信息;「浏览量」来自 Umami 公共接口,本地环境和未部署版本不要期待有正式数据。
第 12 步:检查 404
访问一个不存在的正式 URL,例如:
https://www.baecon.cn/this-page-should-not-exist-20261108检查:
- 显示站点自己的 404 页面;
- 不会把不存在的路径错误地显示成首页正文;
- 顶部导航仍可使用;
- 不会出现构建错误堆栈或敏感信息。
删除旧文章后,旧 URL 默认也不会自动跳到新文章。需要重定向时必须由站点维护者在主机或 CDN 层配置。
上线验收常见错误
| 错误 | 结果 | 正确处理 |
|---|---|---|
| 用 localhost 验收 | 看不到正式域名、CDN、统计和评论状态 | 使用 https://www.baecon.cn |
| 只检查首页 | 文章详情、搜索、侧栏、RSS 可能仍有问题 | 按本节逐项检查 |
| 看到首页旧卡片就判定失败 | 可能仍处于 CDN/浏览器缓存 | 先确认部署成功,再清缓存或硬刷新 |
| 把目录页当成文章 | 目录页本来不显示评论、不进搜索和首页卡片 | 按页面类型判断预期行为 |
清理 EdgeOne 缓存与强制刷新(可选)
部署成功后,大多数情况下新版本会自动生效。如果你确认 CNB 和 EdgeOne 都已成功,但浏览器仍看到旧页面,才进行这一步。
第 1 步:区分两种缓存
| 缓存 | 位置 | 清除方式 |
|---|---|---|
| 浏览器缓存 | 你的电脑或手机 | 硬刷新、无痕窗口、清除站点数据 |
| EdgeOne CDN 缓存 | 云边缘节点 | 在 EdgeOne 控制台刷新 URL 或目录 |
强制刷新浏览器不会清除 EdgeOne 的 CDN 缓存;反过来,刷新 CDN 也不会自动清掉你手机里的浏览器缓存。
第 2 步:在 EdgeOne 控制台刷新
如果确认是 CDN 旧版本:
- 登录 EdgeOne 控制台;
- 找到
my-blog-teek对应的站点或部署项目; - 查找缓存刷新、刷新 URL、目录刷新或版本部署相关入口;
- 优先只刷新问题 URL;不确定影响范围时再刷新全站;
- 等待刷新任务完成。
控制台的具体菜单名称可能随平台版本变化,以实际界面为准。不要为了找按钮去修改源码或删除 dist。
第 3 步:浏览器硬刷新
Windows 常用:
Ctrl+F5macOS 常用:
Cmd+Shift+R仍然怀疑缓存时,使用无痕窗口或直接换一个浏览器测试。手机端可以关闭页面、清理站点数据后再打开。
什么时候不需要刷新缓存
- 部署尚未完成;
- CNB 日志显示构建或审计失败;
- 页面本来就没有提交到 Git;
- 图片文件根本没有上传;
- 路由本身写错。
这些情况下刷新缓存不会修好问题,应先修复源头并重新部署。
回滚与误删恢复
回滚是把线上恢复到之前可用状态。先判断问题属于「源码内容问题」还是「部署产物问题」,再选择方式。
方式一:用 git revert 撤销某次提交
适用场景:某篇文章、某次配置修改导致了内容错误,需要从源码层恢复。
步骤:
- 查看最近的提交:
git log --oneline -5- 找到要撤销的提交短哈希,例如
abc1234:
git revert abc1234- Git 会创建一个新的 revert 提交。若提示冲突,按冲突文件逐项解决,不要盲目删除文件。
- 检查工作区:
git status --short
git log --oneline -3- 推送 main:
git push origin main- 等待 CNB 重新构建和 EdgeOne 部署,再按上线验收清单检查。
为什么用 git revert?它保留历史,并生成一个可审计的新提交,适合多人共享的 main 分支。不要习惯性使用:
git reset --hard
git push --force这些命令会改写历史或覆盖远端,对共享分支风险很高。
方式二:使用 EdgeOne 的部署版本回滚
适用场景:源码大致正确,但当前静态产物出现紧急问题,需要快速回到上一版网站。
如果 EdgeOne 控制台提供部署历史或版本回滚:
- 登录 EdgeOne 控制台;
- 找到
my-blog-teek项目; - 查看部署历史;
- 选择上一个已知正常的版本进行回滚;
- 检查正式域名。
注意:
- 版本回滚只改变线上静态产物,不会改变 Git 源码;
- 下一次有人推送 main 并部署时,网站可能再次变成当前源码的新版本;
- 不同平台和历史版本的实际可用性不同,界面没有该能力时不要假设一定有。
Markdown 删除不会自动回滚
删除一篇文章并推送 main 后,构建会生成一个不再包含该文章的新版本。结果是:
- 新构建中该 route 消失;
- 旧 URL 通常显示 404;
- sitemap 和 RSS 会在新构建中更新;
- 如果 CDN 或浏览器还有缓存,短时间内可能仍看到旧页面;
- 不会自动把旧 URL 重定向到另一篇文章;
- 不会自动恢复被删除的 Markdown。
要恢复被误删的文章,用 git revert 撤销删除提交,或从 Git 历史找回文件后重新提交。要永久废弃一个 URL 并跳转到新地址,需要维护者在主机/CDN 层配置重定向;当前 Markdown 写作流程没有自动重定向机制。
回滚常见错误
| 错误 | 风险 | 正确做法 |
|---|---|---|
在 main 上 reset --hard 后强推 | 覆盖别人的提交,可能造成不可恢复的协作事故 | 用 git revert |
| 只回滚 EdgeOne,不修 Git | 下次部署旧问题会回来 | 同时修复源码并提交 |
| 删除 Markdown 后期望自动恢复 | 新版本会正常地移除页面 | 从 Git 历史恢复 |
| 删除后不检查旧 URL | 访客可能遇到 404 | 配置重定向或保留替代页面 |
| 回滚后不清缓存就检查 | 可能仍看到回滚前的页面 | 确认部署完成,必要时刷新 CDN 和浏览器缓存 |
常见问题与故障排查
遇到问题时,先判断故障发生在哪个阶段:
Markdown/frontmatter
→ 本地开发服务器
→ 生产构建
→ SEO 审计
→ 预览
→ Git
→ CNB
→ EdgeOne
→ 线上浏览器/CDN不要一上来就删除 cache、dist 或 fonts。先看终端第一条错误、CNB 第一个失败阶段和浏览器 Network 面板,通常能更快定位。
快速定位表
| 症状 | 优先检查 |
|---|---|
| 页面顶部出现 YAML 文本或构建报 YAML 错误 | frontmatter 起止线和缩进 |
| 审计提示 h1 数量错误 | 正文 H1 是否恰好一个 |
| 图片 404 | docs/public/images/posts/... 与 /images/posts/... 是否对应 |
| 图片无 alt | Markdown 图片方括号是否为空 |
| 文章不进侧边栏 | sidebar、文件位置、是否重启(article 不直接控制侧栏) |
| 文章不进目录页 | path、inCatalogue: false、目录扫描是否重启 |
| 搜不到文章 | pagefind-indexed、是否重新 build |
| 首页不显示卡片 | article、目录、permalink、构建 |
| 评论不显示 | comment、页面类型、外部 Twikoo 请求 |
| 线上仍是旧页面 | 部署是否成功、CDN/浏览器缓存 |
| 本地正常、CNB 失败 | lockfile、Node 版本、未提交文件、secrets |
| 本地没有 Umami 数据 | 统计只在正式域名运行,这是设计行为 |
1. YAML / frontmatter 报错
典型表现:
- 构建直接失败,并指向某个
.md文件; - 页面顶部显示了
title:、description:等原始文本; - 页面布局、标题或摘要没有按预期生效。
为什么会发生:frontmatter 位于 Markdown 文件最顶部,必须使用严格的 YAML 语法。多一个分隔线、缩进错误、用 Tab、键值中有未转义的冒号,都会让解析失败或让字段失效。
逐步排查:
- 打开报错文件,确认第一行是
---,不是空行或 H1; - 确认 frontmatter 内部还有且只有一个结束行
---; - 确认结束行后面空一行,再写
# 正文 H1; - 检查键值冒号后面是否有空格;
- 多行数组使用两个空格缩进;
- 值里包含
冒号+空格时,用英文双引号包住整个值; - 保存后重新运行
pnpm docs:build。
正确示例:
---
title: Markdown 写作基础
description: "前提: 已安装 Node.js 和 pnpm。"
permalink: /posts/技术笔记/markdown-basics
categories:
- 技术笔记
tags:
- Markdown
- VitePress
article: true
sidebar: true
pagefind-indexed: true
comment: true
---错误示例:
---
title: Markdown 写作基础
description: 前提: 已安装 Node.js 和 pnpm。
---
---
# Markdown 写作基础这里同时有两个问题:description 的值里有未加引号的冒号空格,正文前又多了一组分隔线。
常见错误:
- 在 frontmatter 后面又复制了一遍 frontmatter;
- 把
---写成了--或...; - YAML 列表缩进不一致;
- 从网页复制了看不见的特殊引号;
- 把 H1 写在 frontmatter 前面。
如果错误信息没有明确指出文件,先看终端中最后出现的 Markdown 路径,再从那个文件开始检查。
2. 缺少或重复 H1,导致审计失败
典型表现:pnpm docs:audit 出现类似:
/posts/技术笔记/example-article: h1 数量为 0或:
/posts/技术笔记/example-article: h1 数量为 2为什么重要:H1 是页面唯一主标题。VitePress 的 frontmatter title 不是正文 HTML 的 H1;布局页也不会因为写了 layout: page 就自动免除 H1 检查。
逐步排查:
- 在文章正文中查找以单个
#开头的行; - 确保只有一个这样的行;
- 如果出现多个,把次要标题改成
##或###; - 如果没有,在 frontmatter 结束后空一行,添加唯一 H1;
- 重新构建并审计。
可先列出候选行:
git grep -n '^# ' -- docs/posts注意:这个命令也会匹配代码块里的 # 注释或示例标题,不能只看命令结果就机械删除。最终以页面正文中真正被解析的 H1 为准。
目录页、写作指南页同样需要 H1。pagefind-indexed: false 只排除搜索,不会让 SEO 审计跳过 H1 检查。
3. 图片 404 或缺少 alt
图片 404
先确认磁盘文件是否存在:
Test-Path docs\public\images\posts\example-article\editor-preview.webp预期:
True如果返回 False,说明文件不在以为的位置,或者文件名/扩展名不一致。
然后检查 Markdown 引用:
正确映射关系:
磁盘:docs/public/images/posts/example-article/editor-preview.webp
URL: /images/posts/example-article/editor-preview.webp常见原因:
- Markdown 写成了
docs/public/images/...; - 使用了
C:\Users\...; - 把图片放进了
docs/posts/; - 文件名大小写不一致;
- 实际是
.png,链接写成.webp; - 文件名有空格或中文,URL 与实际文件不一致;
- 图片目录没有提交到 Git。
缺少 alt
审计会报告类似:
/posts/...: 1 张图片缺少 altMarkdown 图片的标准格式是:
方括号里必须有具体描述。不要只写 ,也不要用图片、截图这种空泛内容敷衍。手写 <img> 时同样必须写 alt。
本地检查顺序
- 用
Test-Path确认磁盘文件; - 直接用
http://localhost:5174/images/posts/...打开图片; - 打不开时检查路径映射;
- 图片能打开但页面不显示时检查 Markdown 语法和浏览器缓存;
- 重新 build 后运行 audit 检查 alt 属性,并在浏览器确认图片实际加载、alt 有意义。
4. 页面没有出现在侧边栏、目录页、搜索或首页
这几个区域由不同机制生成,不要用一个检查结果推断全部。
| 区域 | 数据来源 | 必要条件 | 刷新方式 |
|---|---|---|---|
| 文章左侧栏 | docs/.vitepress/sidebar.ts 扫描 docs/posts | 文件在 posts 下,sidebar 不为 false;当前扫描器不读取 article | 修改目录或配置后重启 dev |
| 目录页 | Teek 的 catalogue: true 和 path 扫描 | path 指向实际文章目录,文章未设置 inCatalogue: false | 新增/删除文章或目录页后重启 dev |
| Pagefind 搜索 | 构建阶段的搜索索引 | pagefind-indexed: true,重新 build | 重新 build 并重启 preview |
| 首页文章卡片 | 文章集合和文章元数据 | 位于 posts、article: true、permalink 有效 | 重新构建/刷新 |
| 分类/标签/归档 | 文章 frontmatter 与目录 | 文章类型、分类、标签、日期有效 | 重新构建 |
| RSS | RSS 插件过滤 | article 不为 false,非指南目录 | 重新 build |
逐步检查:
- 文件是否在
docs/posts/<分类>/...下,而不是docs/writing-guide/或docs/catalog/; - frontmatter 是否写了
article: true; - 文章左侧栏是否写了
sidebar: true; - 搜索是否写了
pagefind-indexed: true; permalink是否以/posts/开头且唯一;- 新增或删除文件、目录后,是否重启了开发服务器;
- 测试搜索前是否执行了最新构建;
- 目录页的
path是否真的指向文章所在的相对目录。
写作指南、目录页和工具页使用 article: false、pagefind-indexed: false,它们本来就不会进入首页文章卡片和搜索,不应按普通文章判断。
5. Pagefind 搜索索引过期,或预览仍是旧资源
典型表现:
- 新文章已经提交,但搜索不到;
- 搜索能搜到旧标题;
- 预览页面还是修改前的样式或图片;
- 浏览器里引用旧哈希资源,出现样式错乱。
为什么:Pagefind 索引在 pnpm docs:build 阶段生成;预览服务器服务的是已有 dist。只改 Markdown 不重新 build,或只 build 不重启旧的预览服务器,都可能看到旧结果。
正确处理顺序:
- 停止正在运行的预览或开发服务器:
Ctrl+C; - 确认文章 frontmatter 有
pagefind-indexed: true; - 重新构建:
pnpm docs:build- 确认
docs/.vitepress/dist/pagefind/已更新; - 重启预览:
pnpm docs:preview- 用无痕窗口打开或执行硬刷新;
- 搜索一个只在新文章里出现的独特词。
常见错误:
- 在开发页面里测试搜索,却以为开发服务器一定生成完整 Pagefind;
- 改了 dist 里的搜索结果文件,而没有重新 build;
- 把
pagefind-indexed: false的指南页当成应该能被搜索的文章; - 只刷新浏览器,不重启旧预览进程。
6. 目录页内容要重启开发服务器才刷新
典型表现:新增文章后,文章详情页已经能打开,但 /catalog/... 目录页仍看不到它;重启 pnpm docs:dev 后才出现。
为什么:Teek 的目录页根据 catalogue: true 和 path 扫描文章目录。这个扫描在开发服务器启动或配置加载阶段完成,不是每个 Markdown 文件的 HMR 更新范围。
处理步骤:
- 在开发终端按
Ctrl+C; - 重新启动:
pnpm docs:dev- 打开目录页刷新确认;
- 如果仍不显示,检查目录页
path是否准确指向docs/posts下的目录; - 检查文章是否设置了
inCatalogue: false,或是否被放在忽略的_开头目录;article: false本身不会从目录页隐藏。
这是当前项目的预期行为,不是需要改 dist 才能修复的 bug。提交前可以在重启后的目录页里确认新文章已被扫描到。
7. 端口被占用
默认端口:
- 开发服务器:5174;
- 预览服务器:4173。
开发配置中 strictPort: false,端口被占用时 Vite 可能自动使用下一个可用端口。因此第一件事是看终端实际打印的 Local: 地址,而不是继续打开旧端口。
处理方式一:使用自动选择的端口
看终端:
➜ Local: http://localhost:5175/就打开 5175。
处理方式二:明确指定新端口
开发:
pnpm docs:dev -- --port 5175预览:
pnpm docs:preview -- --port 4174处理方式三:找出占用端口的进程
Windows PowerShell:
netstat -ano | findstr :5174输出最后一列是 PID。确认那是你自己的旧进程后,可以:
taskkill /PID <PID> /F把 <PID> 换成实际数字。不要在不认识进程时随意结束系统服务。
Git Bash、macOS 或 Linux 可以使用:
lsof -i :5174常见错误:
- 终端已经换到 5175,却一直打开 5174;
- 同时启动多个 dev 或 preview,忘记旧的还在运行;
- 用
--port时少写了传给 pnpm 的--; - 为清端口直接重启电脑,实际上只需结束自己的旧进程。
8. 构建失败
构建失败时不要先改 dist,也不要删除 fonts。按照「第一条错误 → 第一个失败文件 → 最小修改 → 重新构建」处理。
逐步排查:
- 向上滚动终端,找到第一条
error或最开始的失败位置; - 确认 Node.js 版本:
node -v- 确认 pnpm 和依赖:
pnpm -v
pnpm install --frozen-lockfile- 检查报错 Markdown 的 frontmatter、未闭合代码块、容器和时间线;
- 检查是否存在重复/错误 permalink;
- 确认没有修改
docs/public/font/TsukuARdGothicStd-Bold.woff2和字体配置; - 重新构建:
pnpm docs:build常见原因:
| 原因 | 表现 | 处理 |
|---|---|---|
| Node.js 版本过低 | Vite 启动失败或 engine 错误 | 升级到 Node 22 LTS |
| 依赖未安装 | 找不到 vitepress 或插件 | pnpm install |
| lockfile 不一致 | frozen-lockfile 失败 | 恢复 lockfile 或由维护者处理依赖变更 |
| frontmatter/YAML 错误 | 解析失败并指向 Markdown | 修 frontmatter |
| 围栏代码块未闭合 | 后续正文被当成代码 | 检查反引号数量 |
| 容器/时间线未闭合 | 页面结构异常 | 补 ::: |
| 内存不足 | heap out of memory | 关闭占内存程序后重试,不随意改构建脚本 |
| 未提交文件 | 本地有图,CNB 看不到 | 提交图片并检查 Git 状态 |
如果错误信息中包含环境变量、Token 或内部地址,不要把完整日志直接发到公开地方。只截取必要的堆栈和文件名,并对敏感值打码。
9. 评论区不显示
先确认这是不是预期行为:
| 页面类型 | 当前配置 | 评论 |
|---|---|---|
| 普通文章 | comment: true | 应显示 |
| 写作指南 | comment: false、layout: doc | 不显示 |
| 目录页 | comment: false、layout: page | 不显示 |
| 工具页 | comment: false | 不显示 |
逐步排查:
- 检查文章 frontmatter 是否是
comment: true; - 检查文章是否误用了
layout: page或article: false; - 刷新页面,尝试无痕窗口;
- 打开浏览器开发者工具 Network,搜索 Twikoo 或 comment 相关请求;
- 检查请求是否被广告拦截器、网络策略或公司代理阻止;
- 检查浏览器 Console 是否有第三方脚本错误;
- 如果其他文章能显示,只这篇不能,优先比较 frontmatter;
- 如果全站都不能显示,可能是 Twikoo 外部服务或网络问题,联系维护者。
评论来自外部 Twikoo 服务,不是 Markdown 构建的一部分。构建成功不代表评论后端一定可访问。不要把评论服务的私密配置、数据库密钥或管理凭据写进文章或提交到仓库。
10. permalink 写错或重复
permalink 是页面的正式路由。普通文章的 permalink 应该:
- 以
/posts/开头; - 不含
.html; - 不含
.md; - 不写完整域名;
- 全站唯一;
- 与内链、canonical、sitemap 中期望的地址一致。
正确:
permalink: /posts/技术笔记/example-article错误:
permalink: /posts/技术笔记/example-article.html
permalink: docs/posts/技术笔记/example-article
permalink: https://www.baecon.cn/posts/技术笔记/example-article检查重复:
git grep -n '^permalink:' -- docs/posts也可以只输出重复项(PowerShell):
git grep -h '^permalink:' -- docs/posts | Group-Object | Where-Object Count -gt 1 | Select-Object Name,Count如果看到同一个 route 出现两次,找到两个源文件,修改其中一个。不要通过修改 dist 或 sitemap 绕过。
为什么重要:
- 侧边栏、搜索清单和目录页可能按 permalink 生成链接;
- 博客文章路由和 canonical 前缀依赖正确 permalink;
- 重复 permalink 会导致路由冲突或错误覆盖;
- 修改 permalink 会改变文章 URL,旧 URL 不会自动重定向。
修改 permalink 后,重新运行:
pnpm docs:build
pnpm docs:audit并检查预览中的新地址、首页卡片、侧边栏、搜索和 canonical。
11. 删除文章后旧 URL 还在,或变成 404
这是两个不同现象。
旧 URL 仍能打开:常见原因是 CDN 或浏览器缓存,不一定是文章还在构建产物里。先确认:
- 源文件确实已从
docs/posts/删除; - 删除已提交并推送到 main;
- CNB 构建和 EdgeOne 部署已成功;
- 新构建的 dist 和 sitemap 不再包含该页面;
- 再检查 EdgeOne 缓存和浏览器缓存。
旧 URL 变成 404:如果文章被删除,新部署不再生成对应 HTML,旧 URL 默认就会 404。这是静态站点的正常结果,不是自动错误。
当前项目没有在 Markdown 层配置自动重定向机制。若希望旧 URL 跳到新文章:
- 需要维护者在 EdgeOne/CDN 或托管平台配置重定向;
- 需要确认重定向规则优先级和缓存行为;
- 仅在文章里写一个链接不能替代 HTTP 重定向;
- 也不要依赖
404.html自动完成跳转,它只是自定义 404 页面。
删除文章后的正确验收顺序:
- 打开旧 URL,确认最终状态符合预期(404 或配置好的重定向);
- 打开新 URL(如果文章是移动而不是删除);
- 检查 sitemap 已移除旧 URL;
- 检查 RSS 已更新;
- 清 CDN/浏览器缓存后再次确认;
- 检查站内是否还有指向旧 URL 的链接。
12. Umami 只有正式域名数据,本地统计被排除
典型表现:本地 http://localhost:5174 或预览端口能看到页面,但 Umami 后台没有对应访问数据;文章浏览量在本地显示为 0 或不显示。
为什么:统计分析脚本通过 data-domains 限制在正式域名运行。当前配置的站点地址是:
https://www.baecon.cn统计允许的域名是:
www.baecon.cn,baecon.cnlocalhost、127.0.0.1 和局域网 IP 不在允许列表中,所以本地访问不会写入正式统计。这是为了避免开发数据污染线上数据。
此外:
- 浏览器开启 Do Not Track 时,Umami 会尊重该设置;
- 广告拦截器、隐私插件或网络策略可能拦截统计脚本;
- 页面浏览量是异步读取的正式统计数据,本地没有数据是预期行为;
- 构建成功不代表统计接口一定被允许访问。
不要这样修:
- 为了在本地看到统计,随意把 localhost 加入正式统计域名;
- 修改
docs/.vitepress/site.ts的正式站点地址; - 用本地测试数据判断线上文章热度;
- 把 Umami Website ID、接口地址或管理凭据当成文章内容发布。
正确验证:
- 确认文章已经部署到正式域名;
- 用普通浏览器访问正式文章,确认没有被拦截;
- 在 Umami 后台查看是否出现该路径;
- 如果正式域名也没有数据,再检查统计脚本请求、域名配置和隐私设置。
13. 字体文件不能修改
这是本项目的硬性约束:
docs/public/font/TsukuARdGothicStd-Bold.woff2docs/.vitepress/ConfigHyde/Head.ts 中对它做了预加载:
<link rel="preload" href="/font/TsukuARdGothicStd-Bold.woff2" as="font" type="font/woff2" crossorigin="">因此不要:
- 修改这个字体文件;
- 重命名或删除它;
- 用其他字体替换它;
- 做字体子集化、压缩或格式转换;
- 修改字体预加载路径来顺便优化;
- 为了修复构建问题删除 font 目录。
即使页面看起来字体不对,也不要先动字体文件。先检查 CSS、主题配置、浏览器缓存和构建日志;确实需要调整时,由了解项目约束的维护者处理。
提交前可以检查字体是否被误改:
git status --short docs/public/font正常情况下这里不应出现你本次文章发布产生的字体改动。
最终发布检查清单
下面的
☐是普通 Unicode 方框,不是可点击控件。复制到任务工具时,可以改成- [ ]。
环境与本地检查
- ☐ 当前目录是仓库根目录;
- ☐
node -v满足^20.19.0 || >=22.12.0,推荐 Node 22 LTS; - ☐
pnpm -v正常; - ☐ 已执行
pnpm install或pnpm install --frozen-lockfile; - ☐
pnpm docs:dev能启动并打开正确 permalink; - ☐ 正文 H1 恰好一个;
- ☐ frontmatter 只有一个区块,且从第一行开始;
- ☐ 图片路径在
docs/public/images/posts/<article-slug>/; - ☐ 图片引用从
/images/posts/<article-slug>/开始; - ☐ 每张图片都有有意义 alt;
- ☐ 站内链接没有
.html、.md、docs/或 localhost; - ☐ 代码块都有语言标签;
- ☐ 容器和时间线都正确闭合;
- ☐ 没有依赖未启用的 Mermaid 或 KaTeX。
构建、审计与预览
- ☐ 已执行
pnpm docs:build并以成功状态结束; - ☐
docs/.vitepress/dist/已生成; - ☐
dist/pagefind/已生成; - ☐
dist/feed.rss和dist/sitemap.xml已生成; - ☐ 构建后执行了
pnpm docs:audit; - ☐ 审计没有阻塞性错误;
- ☐ 理解 34/29 这类页面数量会随新增页面变化,不是固定阈值;
- ☐ 已执行
pnpm docs:preview; - ☐ 预览中检查了页面、图片、代码、搜索和链接;
- ☐ 修改后重新 build 并重启了 preview;
- ☐ 没有直接编辑 dist。
Git 与 CNB
- ☐
git status --short已检查; - ☐
git diff --stat已检查; - ☐ 新增图片已经出现在未跟踪或已暂存列表中;
- ☐ 没有提交
docs/.vitepress/dist/; - ☐ 没有提交
docs/.vitepress/cache/; - ☐ 没有提交
docs/public/search-manifest.json; - ☐ 没有提交
stats.html; - ☐ 没有提交
node_modules/和临时日志; - ☐ 提交里没有 Token、密码或私密配置;
- ☐ 当前分支是 main;
- ☐ 已执行
git push origin main; - ☐ CNB 的安装依赖、构建、SEO Audit 和 EdgeOne 部署阶段成功;
- ☐ 没有把
$EDGEONE_PAGES_API_TOKEN写进源码或公开日志。
线上验收
- ☐ 首页文章卡片显示正确;
- ☐ 文章详情页可打开,URL 不带
.html; - ☐ 目录页能看到新文章;
- ☐ 文章侧边栏链接正确;
- ☐ 站内搜索能搜到应被索引的文章;
- ☐ 评论区符合页面类型和
comment配置; - ☐
/categories、/tags、/archives正常; - ☐
/feed.rss包含新文章; - ☐
/sitemap.xml包含正确 URL 且不含错误页面; - ☐ canonical 指向
https://www.baecon.cn和正确 permalink;中文可能显示为百分号编码,解码后应一致; - ☐ 手机端布局正常;
- ☐ 文章元数据(日期、分类、标签、字数、阅读时长、更新时间)正常;
- ☐ 随机不存在 URL 能显示 404;
- ☐ 如仍有旧页面,已确认部署成功后再刷新 EdgeOne 缓存和浏览器缓存;
- ☐ 如需回滚,使用
git revert或 EdgeOne 版本回滚,没有强推 main; - ☐ 已确认删除 Markdown 不会自动恢复文章,也不会自动生成重定向;
- ☐ 已确认本地没有 Umami 正式统计数据是预期行为;
- ☐ 已确认没有修改
docs/public/font/TsukuARdGothicStd-Bold.woff2。
全部勾选后,这次发布才算完成。

