建站笔记-全站架构进阶篇
这是「进阶版」,假设你已经会 小白篇 → 里的最常见改法和本地预览。 这里讲引擎内部是怎么运作的:目录里每个文件干嘛用、内容怎么变成网页、CI 到底做了什么、怎么安全地自定义。
〇、三套引擎架构对比(一图速查)¶
| 维度 | MkDocs(wiki / digest) | Hugo(nav / works) | Hexo(Civil_Blog) |
|---|---|---|---|
| 内容模型 | 内容驱动:每篇 .md = 一页 | 数据驱动:data/*.yaml + 模板 | 内容驱动:source/_posts/*.md = 文章 |
| 入口配置 | mkdocs.yml(含 nav) | config.toml + layouts/ | _config.yml + themes/butterfly |
| 内容放哪 | docs/ | data/ + layouts/ | source/_posts/ |
| 构建命令 | mkdocs build | hugo | hexo g |
| 本地服务 | mkdocs serve(:8000) | hugo server(:1313) | hexo s(:4000) |
| 产物目录 | site/ | public/ | public/ |
| 部署命令 | mkdocs gh-deploy --force 或 CI | CI(actions-gh-pages) | hexo d |
| 上线分支 | gh-pages(项目页) | gh-pages(项目页) | main(用户页) |
| 自定义点 | overrides/ + extra.css + 插件 | layouts/ + static/ | 主题 _config + 主题覆盖 |
一、MkDocs 架构深潜(civil_wiki + civil_digest)¶
MkDocs 是最典型的"内容驱动":你写 Markdown,它在构建时按 mkdocs.yml 把文章织成站点。
1.1 目录解剖¶
以 civil_digest(结构最简)为例:
civil_digest/
├─ mkdocs.yml # 全局配置 + 导航(nav) + 插件 + 主题
├─ requirements.txt # Python 依赖(mkdocs-material 等)
├─ docs/
│ ├─ index.md # 首页(对应 nav 里的「我的书摘」)
│ ├─ CNAME # 自定义域名(构建时原样拷到 site 根)
│ └─ stylesheets/
│ └─ extra.css # 自定义样式(在 mkdocs.yml 的 extra_css 引用)
├─ overrides/
│ └─ main.html # 主题覆盖(Jinja2,extends base.html)
└─ .github/workflows/ci.yml # 推送 main 时自动 mkdocs gh-deploy --force
civil_wiki 是同一个套路,但体量大得多:docs/ 下按"工作与项目 / 硬件与半导体 / 阅读与学习 …"分了多层文件夹,mkdocs.yml 的 nav 是一棵很深的树,插件也更多(roamlinks、git-revision-date、tags、minify、sitemap hook)。
1.2 内容驱动模型¶
- 每个
docs/**/*.md会被渲染成一个页面,URL 由相对docs/的路径决定(如docs/硬件与半导体/xxx.md→/硬件与半导体/xxx/)。 nav:决定左侧菜单结构和页面标题顺序。nav 里写的路径必须真实存在,否则构建会警告甚至失败。- front-matter(
---之间的title:等)控制单页元信息。
1.3 构建与服务原理¶
mkdocs build:读mkdocs.yml→ 扫描docs/→ 套用 Material 主题 → 输出到site/。mkdocs serve=build+ 起一个本地 HTTP 服务(:8000)+ 监听文件变化自动重建。所以它既能看效果,也是本地开发服务器。- 增删页面后,记得同步
nav:;否则页面能访问,但不会出现在菜单里。
1.4 wiki 与 digest 的差异(同一引擎两种规模)¶
civil_wiki | civil_digest | |
|---|---|---|
nav | 深树(多分区多层级) | 仅首页 |
| 插件 | search/roamlinks/git-revision-date/tags/minify + sitemap hook | 仅 minify |
| 自定义 | overrides/main.html(OO图卡片)+ extra.css(本地中文字体) | 仅 extra.css |
| 适合 | 大知识库 | 轻量书摘 |
1.5 自定义方式¶
- 改样式:在
docs/stylesheets/extra.css写 CSS,并在mkdocs.yml的extra_css引用(wiki 用它换了中文字体、给图片加描边)。 - 改
<head>/ 注入元信息:用overrides/main.html(Jinja2,{% extends "base.html" %}+{% block extrahead %})。wiki 用它加了 Open Graph 社交卡片和字体 preload。 - 加功能:安装插件写进
plugins:(如tags自动聚合标签页)。
1.6 部署内部¶
ci.yml 关键两步:pip install -r requirements.txt → mkdocs gh-deploy --force。 mkdocs gh-deploy 本质是把 site/ 推到仓库的 gh-pages 分支;--force 覆盖旧产物。GitHub Pages 的 Source 设为 gh-pages,于是访问 digest.hanvon.top 看到的就是它。 CNAME 必须放 docs/CNAME(不是仓库根),因为 gh-deploy 只拷贝 docs/ 下内容到 site/ 根。
1.7 常见坑¶
nav指向了不存在的文件 → 构建警告/失败。- 自定义域名写在
docs/CNAME,写错地方(仓库根)不会生效。 - 改了
docs/但没动nav,页面存在却不在菜单里。 - 本地
mkdocs serve正常,但忘了git push→ 线上不变。
二、Hugo 架构深潜(civil_nav + civil_works)¶
Hugo 是数据驱动 + 模板驱动:内容不在 content/ 的一篇篇 md 里,而在 data/*.yaml,由 layouts/ 的 Go 模板"填"出来。
2.1 目录解剖¶
civil_works/
├─ config.toml # 站点标题、disableKinds、params
├─ data/
│ └─ projects.yaml # ★ 作品数据(你主要改这里)
├─ layouts/
│ └─ index.html # ★ 整页模板(Go template,无主题)
├─ static/
│ ├─ assets/ # css/js(构建时拷到站点根)
│ ├─ images/ # 作品封面图
│ └─ favicon.svg # 站点图标
├─ content/
│ └─ _index.md # 首页桩文件(Hugo 需要它来渲染首页)
├─ .github/workflows/ci.yml # actions-hugo 构建 + actions-gh-pages 部署
└─ static/CNAME # 自定义域名(Hugo 原样拷到 public 根)
civil_nav 结构完全一样,只是 data/links.yaml(导航数据)和 layouts/index.html(导航模板)不同——nav 的模板更复杂,复刻了 Webstack 风格的侧边栏 + 卡片网格,并引用 static/assets/ 下下载自参考站的 Bootstrap/xenon CSS 与 JS。
2.2 数据驱动模型¶
data/projects.yaml里是一个结构化列表;layouts/index.html用 Go 模板遍历它:- 你只改
data/*.yaml,模板和样式都不用动。这就是"数据驱动"的好处:内容与表现分离。 config.toml的disableKinds = ["taxonomy","term","RSS"]关掉 Hugo 默认的分类/标签/RSS 页——本站只渲染首页筛选网格,不需要它们(不关的话会刷 WARN)。
2.3 Go 模板速记¶
| 写法 | 含义 |
|---|---|
{{ .Site.Data.projects.items }} | 取 data/projects.yaml 的 items 列表 |
{{ range . }}{{ .name }}{{ end }} | 遍历列表,取每项 name |
{{ with .image }}…{{ end }} | 进入后 . 变成 image 的值(见 2.6 坑) |
{{ if .image }}…{{ .name }}…{{ end }} | 条件判断,. 仍是当前 item |
{{ delimit .categories " " }} | 把 categories 数组拼成空格分隔字符串 |
2.4 nav 与 works 的布局差异¶
- nav:
layouts/index.html是 Webstack 风格——左侧固定深色 sidebar(站点名 + GitHub 链接)+ 右侧按sections分区的卡片网格(.xe-widget.xe-conversations.box2白卡 + 左侧蓝条)。CSS/JS 来自static/assets/(xenon/bootstrap/fontawesome/Arimo),最后加载的nav.css负责卡片悬浮效果。 - works:
layouts/index.html是轻量过滤网格——顶部#myBtnContainer筛选按钮(Show all + 各分类)+.row里.column {{ delimit .categories " " }}+.content(封面图/标题/描述/Website+Repo 按钮组)。样式用参考站真实的static/assets/index.css,交互用static/assets/index.js的filterSelection()。
2.5 构建与服务原理¶
hugo:读config.toml+data/+layouts/+static/→ 输出public/(hugo --minify顺带压缩)。hugo server:构建 + 本地服务(:1313)+ 热更新。static/下的一切(css/js/img/字体/CNAME)会被原样拷贝到public/根,所以模板里引用/assets/index.css即可。
2.6 真实调试案例:image 字段导致的构建崩溃¶
我曾写:
当某作品没有image 时一切正常;一旦加了 image:,with 把上下文 . 变成了图片字符串,再取 .name 就报错:can't evaluate field name in type string。 修复:把 with 改成 if——if 不切换上下文,. 仍是当前 item: 教训:在模板里要取"其他字段"时,用 if 而不是 with(除非你明确只想用那个字段本身)。 2.7 常见坑¶
- 忘了
content/_index.md→ 首页不渲染。 baseURL写错 → 资源 404(本地看不出,上线才暴)。static/CNAME漏了 → 自定义域名失效(Hugo 的 CNAME 在static/,不是docs/)。- 模板里
with误用导致字段取不到(见 2.6)。 - FontAwesome 用 CDN 时离线环境图标消失(可改本地化)。
三、Hexo 架构深潜(Civil_Blog)¶
Hexo 是专为博客设计的内容驱动引擎,按时间组织文章。
3.1 目录解剖¶
Civil_Blog/
├─ _config.yml # 站点信息 + theme + deploy + url
├─ package.json # Node 依赖(hexo / hexo-deployer-git / 主题依赖)
├─ source/
│ ├─ _posts/ # ★ 博客文章(每篇一个 .md,带 front-matter)
│ └─ CNAME # 自定义域名(blog.hanvon.top)
├─ themes/
│ └─ butterfly/ # 主题(不要直接改这里,用主题自带的 _config 覆盖)
├─ scaffolds/ # 新建文章/页面的模板
└─ (无 .github/workflows) # 手动 hexo d 部署,没有 CI
3.2 内容模型¶
source/_posts/年份-月份-日期-标题.md每篇 = 一篇文章。- front-matter 至少要有
title/date/tags: - Hexo 据此生成首页时间线、归档页、标签页、分类页。
3.3 命令链(clean → g → s → d)¶
| 命令 | 作用 |
|---|---|
hexo clean | 删除 db.json 和 public/(清缓存,改了配置或主题后必跑) |
hexo g (generate) | 构建,输出 public/ |
hexo s (server) | 本地服务 http://localhost:4000(热更新) |
hexo d (deploy) | 执行 _config.yml 里的 deploy 把 public/ 推到目标 |
日常开发:hexo clean && hexo g && hexo s 本地看;发布:hexo d。
3.4 部署内部(为什么是 main)¶
_config.yml 里:
hexo d 调 hexo-deployer-git,把 public/ 推到 wild-civil.github.io 的 main 分支根目录。而该仓库名恰好是 <用户名>.github.io——属于用户页,GitHub 默认从 main 根 serve。所以博客"就是 main",和前面项目页走 gh-pages 不同(详见 分支区别 →)。 3.5 常见坑¶
- 第一次要用前必须
npm install(装 Node 依赖)。 - 改了主题配置要
hexo clean再hexo g,否则旧缓存作祟。 - 不要直接改
themes/butterfly/源码,升级主题会丢;用主题提供的覆盖机制。 node版本不兼容可能导致hexo命令报错,按主题要求选 LTS 版本。
四、统一的"静态服务"方式与 file:// 之坑¶
三个引擎的产物(site/ / public/)都不能用 file:// 双击打开:页面引用资源用的是绝对路径(如 /assets/xxx.css、/js/yyy.js),file:// 协议下根路径不对,会样式全崩、JS 全挂。
正确"纯静态看产物"的方式(不依赖引擎热更新):
| 引擎 | 构建 | 起静态服务 |
|---|---|---|
| MkDocs | mkdocs build(出 site/) | cd site && python -m http.server 8000 |
| Hugo | hugo --minify(出 public/) | cd public && python -m http.server 1313 |
| Hexo | hexo g(出 public/) | cd public && python -m http.server 4000 |
但日常开发更推荐直接用引擎自带的 serve/server(mkdocs serve / hugo server / hexo s),它们带热更新,省去手动起 HTTP 服务。
五、部署管线一句话总结¶
- MkDocs(wiki/digest):
git push→ GitHub Actions 跑mkdocs gh-deploy --force→ 推gh-pages→ Pages 显示。 - Hugo(nav/works):
git push→ GitHub Actions 跑hugo --minify+actions-gh-pages→ 推gh-pages→ Pages 显示。 - Hexo(博客):
hexo d→hexo-deployer-git推main→ 用户页直接 serve。
六、交叉阅读¶
- 小白操作版 → 全站架构小白篇 →
- 多站聚焦操作 → 多站架构与本地预览 →
- 引擎怎么选 → 博客引擎的选择 →
- 项目页 vs 用户页 / gh-pages vs main → GitHub Pages 分支区别 →
- 从零搭 MkDocs → 从零搭建多站点 MkDocs →