Skip to content

MkDocs 配置指北

前言:为什么选择 MkDocs?

写论文写麻了,开摸!

忘了哪次摸的时候就看到一个博客还挺好看的,仔细看了一下也是用Markdown构建的静态网站,之前就蠢蠢欲动换平台,这次终于找到机会动手了(。

以前使用的平台Markdown-Blog,确实很简单易用,但也有一些不尽如人意的地方,比如没有侧边栏目录、内部文档跳转似乎也有问题。经过一番调研,发现Material for MkDocs确实不错,于是决定尝试并记录一下部署的过程。

MkDocs 是什么?

MkDocs 是一个用于构建项目文档的工具,专注于将 Markdown 文件转换为静态网站。它的技术特点包括:

  • 由 Python 编写
  • 使用 Python-Markdown 解析 Markdown 文件
  • 使用 Pygments 进行代码语法高亮
  • 使用 Jinja2 模板引擎渲染 Markdown 文件

Material 主题是什么?

Material 主题是基于 Material Design 的 MkDocs 主题,提供了以下特性:

  • 简洁、现代的界面设计
  • 多语言支持
  • 自定义主题颜色
  • 内置搜索功能
  • 灵活的导航和侧边栏
  • 支持标签和分类
  • 集成评论系统
  • 支持 Google Analytics、Disqus 等插件

安装与配置

安装 MkDocs

通过 pip 安装 MkDocs,国内用户可使用清华大学镜像源:

# 标准安装
pip install mkdocs

# 使用清华大学镜像源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs

快速上手

第一步:创建项目

mkdocs new my-project

这将在当前目录下创建一个 my-project 文件夹,包含: - docs 文件夹:用于存放 Markdown 文件 - mkdocs.yml:MkDocs 配置文件 - docs/index.md:网站首页

第二步:启动本地服务器

mkdocs serve

启动后,可以通过 http://127.0.0.1:8000/ 预览网站。MkDocs 的内置服务器支持实时刷新,每次保存文件都会自动更新。

第三步:构建静态网站

mkdocs build

这将在 site 文件夹下生成静态网站,可以部署到服务器。

部署建议

推荐只上传原始文件到服务器,然后在服务器上构建网站。这是因为 MkDocs 版本可能会变化,本地构建的网站可能在服务器上无法正常显示。

如果使用 Git,可以在 .gitignore 文件中添加 site/,避免上传构建后的静态文件。

MkDocs 配置文件详解

mkdocs.yml 是 MkDocs 的核心配置文件,可以配置网站的各种属性:

# 基本信息
site_name: My Project
site_description: A short description of my project
site_author: Your Name
site_url: https://example.com
site_dir: site
site_favicon: images/favicon.ico

# 其他配置项(根据需要添加)

配置建议

强烈推荐使用 Material 主题,并基于其模板进行配置修改。

写作与内容管理

  • 使用 Markdown 格式编写文档
  • 默认文件存放在 docs 文件夹
  • 可以通过 docs_dir 配置自定义文件存放位置
  • mkdocs.yml 中配置导航栏和侧边栏

Material 主题详细配置

安装 Material 主题

# 标准安装
pip install mkdocs-material

# 使用清华大学镜像源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mkdocs-material

# 可能需要的额外插件
pip install mkdocs-minify-plugin
pip install mkdocs-static-i18n

主题配置

Material 主题提供了丰富的配置选项,可以根据个人需求进行深度定制。具体配置可参考 Material-MkDocs 配置文件 相关文档。

Material 主题注意事项

  • 官方文档非常详细,值得仔细阅读
  • 一些高级功能(如 landing page)可能需要成为赞助者才能使用
  • 多语言切换功能存在一些限制,可能需要额外开发

部署方案

mkdocs build构建的静态网站不知道为什么部署不上,暂时先用这堆丑陋的方案处理

Caddy 反向代理配置

Caddyfile 配置

vi /etc/caddy/Caddyfile
# /etc/caddy/Caddyfile

yourdomain.cn {
    encode zstd gzip
    reverse_proxy 127.0.0.1:8000 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {http.request.remote.host}
        header_up X-Forwarded-For {http.request.remote.host}
        header_up X-Forwarded-Port {http.request.port}
        header_up X-Forwarded-Proto {http.request.scheme}
        header_up Accept-Encoding identity
    }
}

服务器启动脚本 server_up.sh

在合适的目录下新建server_up.sh

vi /home/lighthouse/my-project/server_up.sh
#!/bin/bash
export PATH=$PATH:/home/lighthouse/.local/bin
ps aux | grep mkdocs | grep -v grep | awk '{print $2}' | xargs kill -9
cd /home/lighthouse/my-project
mkdocs serve

Systemd 服务配置 mkdocs.service

创建mkdocs.service文件

sudo vi /etc/systemd/system/mkdocs.service
[Unit]
Description=Mkdocs Server

[Service]
ExecStart=/home/lighthouse/my-project/server_up.sh
Restart=always
User=lighthouse

[Install]
WantedBy=multi-user.target

启动服务

开启服务并设置开机自启动

sudo systemctl enable mkdocs
sudo systemctl start mkdocs

参考资料

  1. MkDocs 官方文档
  2. Material 主题文档
  3. 使用 MkDocs 和 Material 主题搭建技术博客 - Eureka!

附录

Material-MkDocs配置文件

site_name: Elros’ Blog
site_description: >- # 项目描述
  Elros’ Blog
site_author: Elros Yip
site_url: https://localhost:8000/ # 我在nginx中使用的是8000端口,如果你使用的是80端口,可以直接写成https://localhost/。

# 代码仓库信息
# repo_name: Egolas/blog # 仓库名称
# repo_url: https://github.com/Egolas/blog.git/ # 仓库地址

# 版权信息
copyright: Copyright &copy; 2023 - 2025 Elros Yip  <div><a href="https://beian.miit.gov.cn/" target="_blank">沪ICP备2023029347号-1</a>  | <a href="https://beian.mps.gov.cn/" target="_blank">沪公网安备31011402006293</a></div> 

# 配置
theme:
  # custom_dir: material/overrides # 自定义文件夹,对于个别页面,如果你不想使用主题的默认样式,可以在这里进行修改,使用里面的文件覆盖主题的默认文件。具体可以参考material官方文档。
  name: material # 主题名称,Material已经是最优秀的选择了,相信我。
  logo: static/images/elrosblog.png # logo 图片
  language: en # 默认语言
  features: # 功能  
    # - announce.dismiss # 可以叉掉公告的功能
    - content.action.edit # 编辑按钮,似乎没啥用
    - content.action.view # 查看按钮,似乎没啥用
    - content.code.annotate # 代码注释,具体不清楚
    - content.code.copy # 复制代码按钮
    # - content.code.select # 选择代码按钮
    # - content.tabs.link # 链接标签
    - content.tooltips # 不太清楚呢这个
    # - header.autohide # 自动隐藏header
    - navigation.expand # 默认展开导航栏
    - navigation.footer # 底部导航栏
    - navigation.indexes # 索引按钮可以直接触发文件,而不是只能点击其下属选项浏览,这个功能可以给对应的section提供很好的预览和导航功能
    # - navigation.instant # 瞬间加载 - 如果这个开着,那么语言切换后就会跳转至首页,所以我关掉了
    - navigation.instant.prefetch # 预加载
    - navigation.instant.progress # 进度条
    - navigation.path # 导航路径, 目前好像没啥用
    # - navigation.prune # 只构建可见的页面
    - navigation.sections # 导航栏的section
    - navigation.tabs # 顶级索引被作为tab
    - navigation.tabs.sticky # tab始终可见
    - navigation.top # 开启顶部导航栏
    - navigation.tracking # 导航栏跟踪
    - search.highlight # 搜索高亮
    - search.share # 搜索分享
    - search.suggest # 搜索建议
    - toc.follow # 目录跟踪-页面右侧的小目录
    # - toc.integrate # 目录跟踪集成到左侧大目录中
  palette:
    - media: "(prefers-color-scheme)" # 主题颜色
      scheme: slate
      primary: black
      accent: indigo
      toggle:
        icon: material/link
        name: Switch to light mode
    - media: "(prefers-color-scheme: light)" # 浅色
      scheme: default
      primary: indigo
      accent: indigo
      toggle:
        icon: material/toggle-switch
        name: Switch to dark mode
    - media: "(prefers-color-scheme: dark)" # 深色
      scheme: slate
      primary: black
      accent: indigo
      toggle:
        icon: material/toggle-switch-off
        name: Switch to system preference
  font: # 字体,大概率不需要换
    text: Roboto
    code: Roboto Mono
  favicon: static/images/elrosblog.png # 网站图标 似乎不需要管
  icon: # 一些用到的icon
    logo: logo
    previous: fontawesome/solid/angle-left
    next: fontawesome/solid/angle-right
    tag:
      default-tag: fontawesome/solid/tag
      hardware-tag: fontawesome/solid/microchip
      software-tag: fontawesome/solid/laptop-code

# Plugins
plugins:
  - tags # 标签功能插件
  - blog: # 注意跟i18n插件冲突,只能启用一个
      enabled: true
      blog_dir: blog
      blog_toc: true
  # - rss: # rss订阅插件 - 不太懂是干嘛的目前
  #     match_path: blog/posts/.* 
  #     date_from_meta:
  #       as_creation: date
  #     categories:
  #       - categories
  #       - tags 
  # - social # 目前我开启会报错,还没研究透 
  - search: # 搜索插件
      separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' # 分隔符
  - minify: # 压缩插件
      minify_html: true
  # - privacy # 隐私插件
  # - i18n: # 多语言插件 注意跟blog插件冲突,只能启用一个
  #     docs_structure: suffix # 抄来的,不太懂
  #     fallback_to_default: true # 抄来的,不太懂
  #     reconfigure_material: true # 抄来的,不太懂
  #     reconfigure_search: true # 抄来的,不太懂
  #     languages: # 多语言配置 - 需要小心一点
  #       - locale: en
  #         default: true # 默认语言
  #         name: English
  #         build: true # 是否构建
  #         # site_name: Infinity
  #       - locale: zh
  #         name: 简体中文
  #         build: false
  #         nav_translations: # 导航栏翻译,不可以有缩进
  #           HOME: 首页
  #           WORKPLACE: 工作区
  #           NETWORK: 网络
            # SPONSORSHIP: 赞助
            # CS: 计算机
            # CODING: 编程
            # EMBEDDED-SYS: 嵌入式系统
            # DSP: 数字信号处理
            # PERCEPTION: 感知
            # ACTUATION: 执行
            # IOT: 物联网
            # CLOUD: 云
            # CLOUD-TECH: 云技术
            # HANDS-ON: 上手实践
            # Have A Server: 拥有一台服务器
            # Server Configuration: 服务器配置
            # Get A Domain Name: 获得一个域名
            # AI: 人工智能
            # RESEARCH: 研究
            # PROJECT: 项目
# Hooks - 讲真,这个东西我还没搞懂
# hooks:
#   - material/overrides/hooks/shortcodes.py
#   - material/overrides/hooks/translations.py

# 额外配置项
extra:
  generator: false # 是否显示生成器
  status: # 不是很懂有什么用
    new: Recently added
    deprecated: Deprecated
  # analytics: # 分析工具, 我反正没用到
  #   provider: google
  #   property: !ENV GOOGLE_ANALYTICS_KEY
  #   feedback: # feedback form
  #     title: Was this page helpful?
  #     ratings:
  #       - icon: material/thumb-up-outline
  #         name: This page was helpful
  #         data: 1
  #         note: >-
  #           Thanks for your feedback!
  #       - icon: material/thumb-down-outline
  #         name: This page could be improved
  #         data: 0
  #         note: >- 
  #           Thanks for your feedback! Help us improve this page by
  #           using our <a href="..." target="_blank" rel="noopener">feedback form</a>.
  # alternate: # 由上面那个i18n插件提供的多语言功能,这个似乎就不需要了。 这个是官方文档的例子,但是官方没有提供很详细的例子,所以我也不知道怎么用。
  #   - name: English
  #     link: /en/ 
  #     lang: en
  #   - name: Chinese
  #     link: /zh/
  #     lang: zh
  # social: # 社交媒体
  #   - icon: fontawesome/solid/house
  #     link: http://www.cuishuaiwen.com/
  #   - icon: fontawesome/brands/github
  #     link: https://github.com/Shuaiwen-Cui
  #   - icon: fontawesome/brands/linkedin
  #     link: https://www.linkedin.com/in/shaun-shuaiwen-cui/
  #   - icon: fontawesome/brands/researchgate
  #     link: https://www.researchgate.net/profile/Shuaiwen-Cui
  #   - icon: fontawesome/brands/orcid
  #     link: https://orcid.org/0000-0003-4447-6687
  #   - icon: fontawesome/brands/twitter
  #     link: https://twitter.com/ShuaiwenC
  tags: # 自定义标签
    Default: default-tag
    Hardware: hardware-tag
    Software: software-tag
  # consent: # 征求同意 Cookie
  #   title: Cookie consent
  #   description: >- 
  #     We use cookies to recognize your repeated visits and preferences, as well
  #     as to measure the effectiveness of our documentation and whether users
  #     find what they're searching for. With your consent, you're helping us to
  #     make our documentation better.

# 扩展
markdown_extensions: # markdown extensions
  - abbr
  - admonition
  - attr_list
  - def_list
  - footnotes
  - md_in_html
  - admonition #MkDocs 警告框
  - footnotes #脚注
  - meta #自定义文章元数据
  - toc:
      permalink: true
  - pymdownx.arithmatex:
      generic: true
  - pymdownx.betterem:
      smart_enable: all
  - pymdownx.caret
  - pymdownx.details
  - pymdownx.emoji:
      emoji_generator: !!python/name:material.extensions.emoji.to_svg
      emoji_index: !!python/name:material.extensions.emoji.twemoji
  - pymdownx.highlight:
      anchor_linenums: true
      line_spans: __span
      pygments_lang_class: true
  - pymdownx.inlinehilite
  - pymdownx.keys
  - pymdownx.magiclink:
      normalize_issue_symbols: true
      repo_url_shorthand: true
      user: squidfunk
      repo: mkdocs-material
  - pymdownx.mark
  - pymdownx.smartsymbols
  - pymdownx.snippets:
      auto_append:
        - includes/mkdocs.md
  - pymdownx.superfences:
      custom_fences:
        - name: mermaid
          class: mermaid
          format: !!python/name:pymdownx.superfences.fence_code_format
  - pymdownx.tabbed:
      alternate_style: true
      combine_header_slug: true
      slugify: !!python/object/apply:pymdownx.slugs.slugify
        kwds:
          case: lower
  - pymdownx.tasklist:
      custom_checkbox: true
  - pymdownx.tilde

nav: 
  - HOME: 
    - "./index.md"
  - WORKPLACE: 
    - MacOS: 
      - Aria2: "./workplace/MacOS/Mac下配置Aria2.md"
    - Linux:
      - Tmux: "./workplace/Linux/TMUX.md"
  - NETWORK: 
    - Zerotier Private Planet: "./network/私有化部署Zerotier Planet Server.md"
    - IPv6-NAT6: "./network/校园网环境下的OpenWrt NAT6配置.md"
  - BLOG: 
    - Blog: "./blog/index.md"

语法速查

Tip

可以参考material主题文档中的写作内容部分,非常多值得借鉴的内容

以下句法可以重点学习:

  • Note

    !!! note
        This is a note.
    

  • Tip

    !!! tip
        This is a tip.
    

  • Warning

    !!! warning
        This is a warning.
    

  • Danger

    !!! danger
        This is a danger.
    

  • Success

    !!! success
        This is a success.
    

  • Info
    !!! info
        This is an info.
    
  • Quote
    !!! quote
        This is a quote.
    
  • Question
    ??? question "What is the meaning of life, the universe, and everything?"
        42.
    
  • Note

Note

This is a note.

  • Tip

Tip

This is a tip.

  • Warning

Warning

This is a warning.

  • Danger

Danger

This is a danger.

  • Success

Success

This is a success.

  • Info

Info

This is an info.

  • Quote

Quote

This is a quote.

  • Question
What is the meaning of life, the universe, and everything?

Nothing.