跳转至

从零搭建MkDocs博客

之前用过 Hexo + Fluid 主题写博客,也试过 VitePress,但总感觉不太满意。Hexo 的 Node.js 生态太重,VitePress 更适合写文档而不是博客。最终选择了 MkDocs + Material for MkDocs,搭建过程记录如下。

为什么选 MkDocs Material

方案 优点 缺点
Hexo + Fluid 中文社区成熟,主题好看 Node.js 依赖重,构建慢
VitePress Vue 生态,构建快 文档风格,不适合博客
MkDocs Material Python 生态,博客功能开箱即用,暗色模式 需要 Python 环境

Material for MkDocs 支持目录级组织,配合 navigation.indexes 特性可以按分类目录管理文章,结构清晰且无需额外插件。

环境准备

安装 uv

uv 是 Rust 写的 Python 包管理器,速度极快。

Windows(PowerShell):

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Linux / macOS:

curl -LsSf https://astral.sh/uv/install.sh | sh

验证安装:

uv --version

初始化项目

mkdir my-blog
cd my-blog
uv init --python 3.12
uv add mkdocs-material mkdocs-literate-nav
  • mkdocs-material 会自动安装 mkdocs 本体和所有依赖
  • mkdocs-literate-nav 让导航由各目录的 SUMMARY.md 文件控制,不用在 mkdocs.yml 里手动写 nav

项目结构

my-blog/
├── mkdocs.yml                          # 主配置文件
├── docs/
│   ├── SUMMARY.md                      # 顶层导航(tab 级别)
│   ├── index.md                        # 首页
│   ├── assets/
│   │   ├── images/
│   │   │   └── avatar.png              # Logo 和 Favicon
│   │   └── stylesheets/
│   │       └── extra.css               # 自定义样式
│   ├── overrides/                      # 主题覆盖
│   │   └── partials/
│   │       └── header.html             # 自定义 header(社媒链接)
│   ├── python/
│   │   ├── SUMMARY.md                  # Python 教程导航
│   │   ├── index.md                    # 分类首页
│   │   ├── conda-to-uv.md              # 文章
│   │   └── data-analysis/
│   │       ├── SUMMARY.md              # 子分类导航
│   │       ├── index.md
│   │       └── pandas.md
│   ├── agent/
│   │   ├── SUMMARY.md                  # Agent 导航
│   │   ├── index.md
│   │   └── ...
│   └── tools/
│       ├── SUMMARY.md                  # 工具导航
│       ├── index.md
│       └── ...

关键设计:

  • SUMMARY.md:每个目录一个,控制该目录的导航顺序。新增文章时只需编辑对应目录的 SUMMARY.md
  • overrides/:覆盖 Material 主题模板,用于自定义 header(如社媒链接)
  • index.md:每个分类目录的首页,配合 navigation.indexes 特性作为分类落地页

配置文件完全指南

所有设置都在 mkdocs.yml 中,下面按功能模块逐项说明。

站点基本信息

site_name: 算栗工坊                                      # 站点名称,显示在浏览器标签页
site_description: LLM、Python 开发及算法等方向的思考与实践    # 站点描述,用于 SEO
site_url: https://suanlilog.com                           # 站点 URL,用于生成 sitemap
repo_url: https://github.com/suanlilog                    # 右上角仓库链接
repo_name: suanlilog                                      # 仓库名称显示文字
  • site_name 会出现在浏览器标签页标题中
  • repo_url + repo_name 会在导航栏右上角显示 GitHub 图标链接

主题配置

theme:
  name: material
  custom_dir: docs/overrides                # 主题覆盖目录
  language: zh                              # 界面语言设为中文
  logo: assets/images/avatar.png            # 左上角 Logo 图片
  favicon: assets/images/avatar.png         # 浏览器标签页图标
  icon:
    repo: fontawesome/brands/github         # 仓库链接的图标
  • custom_dir 指向覆盖目录,用于自定义 header 等模板
  • Logo 和 Favicon:把头像或品牌图标放到 docs/assets/images/ 目录下,建议用正方形图片,尺寸 100x100 以上

主题色与暗色模式

  palette:
    - scheme: default                   # 亮色模式
      primary: indigo                   # 主色调
      accent: indigo                    # 强调色
      toggle:
        icon: material/brightness-7     # 切换按钮图标
        name: 切换到暗色模式
    - scheme: slate                     # 暗色模式
      primary: indigo
      accent: indigo
      toggle:
        icon: material/brightness-4
        name: 切换到亮色模式
  • 配置两个 palette 项即可实现亮/暗模式切换,右上角会自动出现太阳/月亮图标
  • primary 可选值:redpinkpurpledeep purpleindigobluelight bluecyantealgreenlight greenlimeyellowamberorangedeep orangebrowngreyblue grey
  • accent 同上,用于按钮、链接等强调元素

字体配置

  font:
    text: Noto Sans SC          # 正文字体
    code: Fira Code             # 代码字体
  • text:可选 ubunturobotoopen sansnoto sans sc
  • code:可选 ubuntu monoroboto monofira codesource code pro
  • 字体通过 Google Fonts 加载,国内访问可能较慢,可自行配置 CDN

导航特性

  features:
    # --- 导航栏 ---
    - navigation.tabs                  # 顶部导航标签页
    - navigation.tabs.sticky           # 滚动时导航栏固定在顶部
    - navigation.sections              # 侧边栏分组显示
    - navigation.top                   # 回到顶部按钮
    - navigation.indexes               # 目录 index.md 作为分类首页
    - navigation.instant               # SPA 式无刷新页面切换
    - navigation.instant.progress      # 页面加载顶部进度条
    - navigation.footer                # 文章页显示上一篇/下一篇
    - navigation.tracking              # URL 跟踪,侧边栏自动高亮当前章节

    # --- 搜索 ---
    - search.suggest                   # 搜索时自动补全
    - search.highlight                 # 搜索结果高亮关键词
    - search.share                     # 搜索可分享链接

    # --- 内容 ---
    - content.code.copy                # 代码块复制按钮
    - content.code.annotate            # 代码注解功能
    - content.tabs.link                # 代码标签页同步切换
    - content.tooltips                 # 链接悬浮预览
    - toc.follow                       # 目录跟随滚动自动高亮

常用特性说明:

特性 效果
navigation.tabs nav 的第一层显示为顶部标签页
navigation.tabs.sticky 标签页滚动时固定,不随页面消失
navigation.indexes 目录的 index.md 作为该分类的落地页
navigation.instant 页面间切换无刷新,类似 SPA 体验
navigation.footer 文章底部自动显示上一篇/下一篇链接
content.code.copy 代码块右上角出现复制按钮
toc.follow 右侧目录跟随页面滚动自动高亮当前标题

插件

plugins:
  - search                              # 搜索功能
  - literate-nav                        # 由各目录的 SUMMARY.md 控制导航
  • search:提供站内搜索(Material 主题的搜索功能依赖此插件)
  • literate-nav:让每个目录通过 SUMMARY.md 文件控制导航结构,不用在 mkdocs.yml 里手写 nav

社交链接(导航栏)

社媒链接通过主题覆盖(theme override)固定在每页顶部导航栏,而非 footer。这样无论页面多长,访客都能一眼看到。

实现方式:在 docs/overrides/partials/header.html 中覆盖主题的 header 模板,在搜索图标和仓库链接之间注入社媒图标。

{# 在 header.html 的 search 和 repo_url 之间插入 #}
<div class="md-header__social">
  <a class="md-icon" href="https://github.com/suanlilog" target="_blank" rel="noopener" title="GitHub">
    <svg viewBox="0 0 496 512">...</svg>
  </a>
  <a class="md-icon" href="https://www.zhihu.com/people/suanligongfang" target="_blank" rel="noopener" title="知乎">
    <svg viewBox="0 0 640 512">...</svg>
  </a>
  <a class="md-icon" href="https://xhslink.com/m/5ZOZtkPwIqW" target="_blank" rel="noopener" title="小红书">
    <svg viewBox="0 0 512 512">...</svg>
  </a>
</div>

样式写在 docs/assets/stylesheets/extra.css 中:

.md-header__social {
  display: flex;
  align-items: center;
  gap: 0.1rem;
  margin-left: 0.2rem;
}
.md-header__social a.md-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  color: var(--md-default-fg-color--light);
  transition: color 0.2s ease;
  opacity: 0.7;
}
.md-header__social a.md-icon:hover {
  color: var(--md-primary-fg-color);
  opacity: 1;
}
.md-header__social a.md-icon svg {
  width: 1rem;
  height: 1rem;
  fill: currentColor;
}

SVG 图标使用 Font Awesome Free,常用图标参考:

平台 图标名 viewBox
GitHub github 0 0 496 512
知乎 zhihu 0 0 640 512
B 站 bilibili 0 0 512 512
微信 weixin 0 0 576 512
邮箱 envelope 0 0 512 512

Markdown 扩展

markdown_extensions:
  # --- 基础 ---
  - admonition                        # 告示框(note/warning/tip 等)
  - attr_list                         # 支持为元素添加 HTML 属性
  - def_list                          # 定义列表
  - footnotes                         # 脚注
  - tables                            # 表格
  - toc:
      permalink: true                 # 标题旁显示锚点链接

  # --- 增强 ---
  - pymdownx.details                  # 可折叠的告示框
  - pymdownx.highlight:
      anchor_linenums: true           # 代码块带行号
  - pymdownx.inlinehilite             # 行内代码高亮
  - pymdownx.superfences              # 增强代码块,支持嵌套
  - pymdownx.tabbed:
      alternate_style: true           # 标签页式代码块
  - pymdownx.tasklist:
      custom_checkbox: true           # 任务列表(复选框)

这些扩展配合 Material 主题可以实现丰富的排版效果,详见下方「写作技巧」章节。

自定义样式

extra_css:
  - assets/stylesheets/extra.css      # 引入自定义 CSS

docs/assets/stylesheets/extra.css 中编写自定义样式,可以覆盖主题默认样式。常用自定义:

/* Logo 圆形裁剪 */
.md-header__button.md-logo img {
  border-radius: 50%;
  width: 2rem;
  height: 2rem;
}

/* 页面淡入动画 */
.md-typeset .md-content__inner {
  animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* 告示框圆角 */
.md-typeset .admonition,
.md-typeset details {
  border-radius: 8px;
}

/* 表格圆角 + 表头加粗 */
.md-typeset table:not([class]) {
  border-radius: 8px;
  overflow: hidden;
}
.md-typeset table:not([class]) th {
  background-color: var(--md-primary-fg-color--light);
  font-weight: 600;
}

/* 搜索弹窗毛玻璃 */
.md-search__overlay {
  backdrop-filter: blur(4px);
}

/* 内容区最大宽度 */
.md-grid {
  max-width: 1400px;
}

导航结构

用 SUMMARY.md 管理导航

使用 mkdocs-literate-nav 插件后,导航不再写在 mkdocs.yml 中,而是由每个目录下的 SUMMARY.md 文件控制。

根目录 docs/SUMMARY.md(控制顶部标签页):

- [首页](index.md)
- [Python教程](python/)
- [Agent开发](agent/)
- [工具教程](tools/)

分类目录 docs/python/SUMMARY.md(控制侧边栏):

- [Python教程](index.md)
- [从conda转uv快速上手](conda-to-uv.md)
- [数据分析](data-analysis/)

子分类 docs/python/data-analysis/SUMMARY.md

- [数据分析](index.md)
- [Pandas快速入门](pandas.md)

规则:

  • 目录链接用 目录名/ 格式(如 python/),会自动指向该目录的 index.md
  • 文件链接用 文件名.md 格式
  • 排序完全由 SUMMARY.md 中的顺序决定
  • 新增文章只需:1)放 .md 文件 2)在对应 SUMMARY.md 加一行

分类首页模板

每个分类目录下的 index.md

---
title: Python教程
---

# Python教程

- [从conda转uv快速上手](conda-to-uv.md) — uv 是用 Rust 编写的 Python 包管理器
- [Pandas 快速入门](data-analysis/pandas.md) — 数据分析必备

写作技巧

文章 Front-matter

---
date: 2026-05-22           # 发布日期
tags:                       # 标签(可选)
  - python
  - 工具
---
  • date 用于文章排序和展示
  • tags 可选,用于标记关键词
  • 不需要 categories,分类由目录结构决定

告示框(Admonition)

!!! note "注意事项"
    这是一个注意提示框。

!!! warning "警告"
    这是一个警告提示框。

!!! tip "小技巧"
    这是一个技巧提示框。

类型包括:noteabstractinfotipsuccessquestionwarningfailuredangerbugexamplequote

??? 变成可折叠:

??? note "点击展开详情"
    折叠内容...

代码块

基础用法:

```python
def hello():
    print("Hello, 算栗工坊!")
```

带标题的代码块:

```python title="hello.py"
def hello():
    print("Hello, 算栗工坊!")
```

代码高亮特定行:

```python hl_lines="2 3"
def hello():
    name = "算栗工坊"   # (1)!
    print(f"Hello, {name}!")
```
  1. 这行会被高亮

标签页

=== "Python"

    ```python
    print("Hello")
    ```

=== "JavaScript"

    ```javascript
    console.log("Hello");
    ```

配合 content.tabs.link 特性,切换一个页面的标签页会同步切换所有相同标签页。

任务列表

- [x] 已完成的任务
- [ ] 待完成的任务

表格

| 功能     | 说明           |
| -------- | -------------- |
| 暗色模式 | 右上角切换     |
| 代码复制 | 代码块右上角按钮 |

脚注

这是一段话,后面有脚注[^1]。

[^1]: 这是脚注内容。

常用命令

# 本地预览(自动刷新)
uv run mkdocs serve

# 构建静态文件(输出到 site/ 目录)
uv run mkdocs build

# 部署到 GitHub Pages
uv run mkdocs gh-deploy

部署到 GitHub Pages

  1. 在 GitHub 创建仓库(比如 username.github.io
  2. 在仓库 Settings -> Pages,Source 选择 gh-pages 分支
  3. 本地执行:
git init
git add .
git commit -m "initial commit"
git remote add origin https://github.com/username/username.github.io.git
git push -u origin main
uv run mkdocs gh-deploy

gh-deploy 会自动构建并推送到 gh-pages 分支,几分钟后就能访问了。

完整配置参考

以下是本博客使用的完整 mkdocs.yml

site_name: 算栗工坊
site_description: LLM、Python 开发及算法等方向的思考与实践
site_url: https://suanlilog.com
repo_url: https://github.com/suanlilog
repo_name: suanlilog

theme:
  name: material
  custom_dir: docs/overrides
  language: zh
  logo: assets/images/avatar.png
  favicon: assets/images/avatar.png
  icon:
    repo: fontawesome/brands/github
  palette:
    - scheme: default
      primary: indigo
      accent: indigo
      toggle:
        icon: material/brightness-7
        name: 切换到暗色模式
    - scheme: slate
      primary: indigo
      accent: indigo
      toggle:
        icon: material/brightness-4
        name: 切换到亮色模式
  font:
    text: Noto Sans SC
    code: Fira Code
  features:
    - navigation.tabs
    - navigation.tabs.sticky
    - navigation.sections
    - navigation.top
    - navigation.indexes
    - navigation.instant
    - navigation.instant.progress
    - navigation.footer
    - navigation.tracking
    - search.suggest
    - search.highlight
    - search.share
    - content.code.copy
    - content.code.annotate
    - content.tabs.link
    - content.tooltips
    - toc.follow

extra_css:
  - assets/stylesheets/extra.css

extra: {}

plugins:
  - search
  - literate-nav

markdown_extensions:
  - admonition
  - attr_list
  - def_list
  - footnotes
  - tables
  - toc:
      permalink: true
  - pymdownx.details
  - pymdownx.highlight:
      anchor_linenums: true
  - pymdownx.inlinehilite
  - pymdownx.superfences
  - pymdownx.tabbed:
      alternate_style: true
  - pymdownx.tasklist:
      custom_checkbox: true

踩坑记录

VitePress 中文文件名问题

之前用 VitePress 时踩的坑:VitePress 对中文文件名支持有问题,构建时会报 Cannot read properties of undefined (reading 'imports')。文章文件名一定要用英文,标题写在 frontmatter 里。

Material 图标语法不渲染

Material for MkDocs 的图标语法(如 :material-clock-fast:)需要配置 pymdownx.emoji 扩展才能渲染。如果不想折腾,直接用纯文字或 emoji 就好。

Blog 插件不适合分类博客

最初尝试用 MkDocs Material 的 blog 插件,但发现它会自动排除 blog/ 目录下非 posts/ 的 markdown 文件,导致分类页面无法正常工作。

最终方案:放弃 blog 插件,改用目录组织。文章按分类放入对应目录,每个目录一个 index.md 作为分类首页,配合 navigation.indexes 特性实现分类导航。结构更简单,也不需要折腾插件。

首页模板报错

不要在 frontmatter 里写 template: home.html,Material for MkDocs 没有这个模板。

总结

MkDocs Material 搭建博客非常省心,Python 生态 + 目录级组织,暗色模式、代码高亮、搜索、分类导航全都有。不需要 blog 插件,按目录分类 + navigation.indexes + mkdocs-literate-nav 就能实现清晰的分类导航。相比 Hexo 少了很多折腾,相比 VitePress 更适合写博客。

如果你也在找一个简洁好用的博客方案,推荐试试。