跳转至

静态网站框架mkdocs+material学习笔记

这个静态网站的框架已经从Pelican换成了Mkdocs,搭配mkdocs-material主题,托管在GitHub上,使用自有域名。

把一些技术细节记在这里,备忘。

问题#

Caution

以汉字开头的markdown文件可能导致奇怪的问题。可以数字时间开头,如“2021-03-17”。

安装#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
pip install mkdocs-material
# 以下是插件,按需要安装
pip install mkdocs-mermaid2-plugin
pip install mknotebooks
pip install mkdocs-macros-plugin
pip install mkdocs-git-revision-date-plugin

# pip install mkdocs-redirects

pip install jieba
# pip install click-man
# click-man --target path/to/man/pages mkdocs

生成项目#

1
mkdocs new your\blog\dir

设置文件#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# mkdocs.yml
site_name: 嬉戏实验室
nav: # 导航结构
    - Home: index.md # [定制主页](https://github.com/squidfunk/mkdocs-material/issues/1996)
    - 儿童识字:
        - '2020-06-24-自制儿童启蒙集中识字语料库与分级字表.md'
        - '2020-06-24-自制儿童启蒙集中识字分级字表.md'
    - 老编辑的效率工具:
        - '2018-11-7-奇技淫巧志.md'
        - '两种markdown文件解析方法': '2021-02-04-两种markdown文件解析方法.ipynb' # 原本只支持markdown文件,其他文件需要插件支持,同时可能需要制定标题
    - About: about.md
theme:
    name: material # 指定mkdocs-material模板
    favicon: img/favicon.ico
    logo: img/logo.png
    palette: # 更改配色
        primary: blue grey # 主色
        accent: pink # 强调色
    features:
        - navigation.instant
        - navigation.tabs # 栏头加标签(一级导航)
        - navigation.tabs.sticky # 固定(浏览时不收起)标签
    language: zh
    custom_dir: overrides # 指定模板文件夹,放自定的覆盖模板
markdown_extensions:
    - meta # 支持markdown博客文件头的元数据,比如标题
    - toc:
        permalink: "#"
        baselevel: 1
        separator: "_"
    - footnotes # 支持脚注
    - admonition  # 支持提示块
    - pymdownx.details  # 提示块可折叠
    - attr_list 
    - pymdownx.inlinehilite # 支持行内语法高亮
    # - pymdownx.highlight: # 支持代码块语法高亮,!!!跟mermaid冲突
    #     linenums: true # 显示行号
    - pymdownx.superfences: # 可在列表等处嵌入块
        # make exceptions to highlighting of code:
        custom_fences:
            - name: mermaid
              class: mermaid
              format: !!python/name:mermaid2.fence_mermaid
plugins:
    - mknotebooks:
        enable_default_jupyter_cell_styling: false
        enable_default_pandas_dataframe_styling: false
        # write_markdown: true
    - search: # 搜索
        lang:
            - en
            - ja # 日文,也勉强支持中文
        separator: '[\s\-\.]+' # 分词分隔符
        # prebuild_index: true # 预制索引,不成功
    - mermaid2:
        arguments:
            securityLevel: 'loose'
    - macros:
        # 替换默认的jinja宏标志{{}},否则类似的地方可能报错
        # 或用raw标签包裹
        # [[% raw %]]
        j2_block_start_string: '[[%'
        j2_block_end_string: '%]]'
        j2_variable_start_string: '[['
        j2_variable_end_string: ']]'
        # [[% endraw %]]    
    # - redirects: # 重定向,改动文件名时使用
    #     redirect_maps:
    #         path/to/old/file.md: path/to/new/file.md
# google_analytics: # 谷歌分析
#   - XX-XXXXXXXXX-X
#   - auto
gtag:
  - G-XXXXXXXXXX

extra:
    social: # 社交账号
        - icon: fontawesome/brands/github
          link: https://github.com/Fusyong
        #   name: Fusyong on Github
        - icon: fontawesome/brands/facebook
          link: https://www.facebook.com/Aahuaang/
        #   name: Fusyong on Facebook
        - icon: fontawesome/brands/twitter
          link: https://twitter.com/fusyong
        #   name: Fusyong on Twitter
    # disqus: XXXXXXXX # disqus评论插件

运行服务器#

1
2
cd your\blog\dir
mkdocs serve

生成发布版本#

1
2
cd your\blog\dir\
mkdocs build

or清理文件夹后在生成

1
2
cd your\blog\dir\
mkdocs build --clean

标题取用顺序#

  1. 从配置文件的导航配置nav中;
  2. 博文元数据的title值;
  3. Markdown的第一个1级标题;
  4. 博文的文件名。

主题#

mkdocs-material模板覆盖文件夹overrides的结构#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.
├─ .icons/                             # Bundled icon sets
├─ assets/
│  ├─ images/                          # Images and icons  ├─ javascripts/                     # JavaScript  └─ stylesheets/                     # Stylesheets
├─ partials/
│  ├─ integrations/                    # Third-party integrations    ├─ analytics.html                # - Google Analytics    └─ disqus.html                   # - Disqus  ├─ languages/                       # Localized languages  ├─ footer.html                      # Footer bar  ├─ header.html                      # Header bar  ├─ language.html                    # Localized labels  ├─ logo.html                        # Logo in header and sidebar  ├─ nav.html                         # 主导航  ├─ nav-item.html                    # 主导航条目  ├─ palette.html                     # Color palette  ├─ search.html                      # Search box  ├─ social.html                      # Social links  ├─ source.html                      # Repository information  ├─ source-date.html                 # Last updated date  ├─ source-link.html                 # Link to source file  ├─ tabs.html                        # Tabs navigation  ├─ tabs-item.html                   # Tabs navigation item  ├─ toc.html                         # Table of contents  └─ toc-item.html                    # Table of contents item
├─ 404.html                            # 404错误页
├─ base.html                           # Base template
└─ main.html                           # Default page

下载官方库src中的对应文件,修改后放在上述的本地文件夹overrides中的相应位置。

在导航条目下加一行更新时间#

拷贝官方的nav-item.html文件到overrides\partials\nav-item.html,修改其中的一段如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  <!-- Main navigation item -->
  ...
    <li class="{{ class }}">
      <a href="{{ nav_item.url | url }}" class="md-nav__link">
        {{ nav_item.title }}
      </a>
      <!-- 这里是修改根据markdown文章的meta数据显示Status值为new的文章的Modified时间 -->
      {% if nav_item.meta.Status and nav_item.meta.Status == "new" %}
      <font size=0.5em color="grey">
        {{ nav_item.meta.Modified }} 更新
      </font>
      <br>
      {% endif %}
      <!-- 修改结束 -->
    </li>
  ...

markdown_extensions#

Markdown格式支持:

提示块插件#

设置如下:

1
2
3
markdown_extensions:
    - admonition  # 提示块
    - pymdownx.details  # 提示块可折叠

语法如下:

1
2
3
4
!!! note "可以双引号中指明标题"
    随便多少缩进文本.

    空行分段.

效果如下:

可以双引号中指明标题

随便多少缩进文本.

空行分段.

note用作CSS类名和默认标题,只能是一个单词例如

1
2
!!! hint
    标题会自动大写。

效果如下:

Hint

标题会自动大写。

如果不想要标题则引号内留空""

1
2
!!! important ""
    无标题提示块。

效果是:

无标题提示块。

可以附加额外的CSS类,用空格隔开,会附在type.后:

1
2
!!! danger highlight blink "不要在家里做"
    ...

效果:

共支持这些类型#

  • note, seealso
  • abstract, summary, tldr
  • info, todo
  • tip, hint, important
  • success, check, done
  • question, help, faq
  • warning, caution, attention
  • failure, fail, missing
  • danger, error
  • bug
  • example
  • quote, cite

插件#

markdown增强#

目前支持

  • 行内脚注
  • 字头拼音标注
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import re
from mkdocs.plugins import BasePlugin

class Extended_markdown(BasePlugin):

    def __init__(self):

        # 行内脚注格式`^[脚注内容]`
        self.inline_footnote_pattern = re.compile(r"\^\[([^\[\]]+)\]")
        self.inline_footnote_inplace = r"[^\1]"
        # 替换`[汉字]^(拼音)`表达式为html的ruby标签
        self.pinyin_pattern = re.compile(r"\[([^\[\]]+)\]\^\(([^\(\)]+)\)")
        self.ruby_pattern = r"<ruby>\1<rp>(</rp><rt>\2</rt><rp>)</rp></ruby>"

    # 处理每篇文章的markdown文件
    def on_page_markdown(self, markdown, page, config, files):
        """
        """
        # 替换拼音
        markdown = re.sub(self.pinyin_pattern,
                          self.ruby_pattern,
                          markdown)
        # 抽取行内脚注内容
        footnote_list = (f'[^{m.group(1)}]: {m.group(1)}' 
                         for m in re.finditer(self.inline_footnote_pattern, markdown))
        if footnote_list:
            # 替换行内脚注
            markdown = re.sub(self.inline_footnote_pattern,
                            self.inline_footnote_inplace,
                            markdown)
            # 附加脚注
            markdown = markdown + "\n" + "\n".join(footnote_list)

        return markdown

简单支持中文搜索#

更新:冯唐正先生已经做出相当完善的方案,并已经提交到官方代码库,方案详见A Possible Version for Chinese Search Support #3761

找了一遍,网上为Mkdocs实现中文搜索的方法都不灵,而且挺麻烦。

以下只是简单更改mkdocs-material默认的search组件,对内容进行分词,以支持简单的中文搜索。之所以说“简单”,是说引擎不会为用户分词,用户输入的关键词必须自己用空格分开,像英文那样。

在配置中启用search插件,指定英语、日语和分隔符:

1
2
3
4
5
6
plugins:
    - search:
        lang:
            - en
            - ja
        separator: '[\s\-\.]+'
更改默认的search插件mkdocs.contrib.search.search_index.py,添加有注释的代码行,对内容进行中文分词:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import jieba # 中文分词用模块

class SearchIndex:

    # 以上不变,略

    def _add_entry(self, title, text, loc):
        """
        A simple wrapper to add an entry and ensure the contents
        is UTF8 encoded.
        """
        text = text.replace('\u3000', ' ') # 替换中文全角空格
        text = text.replace('\u00a0', ' ')
        text = re.sub(r'[ \t\n\r\f\v]+', ' ', text.strip())

        # 给正文分词
        text_seg_list = jieba.cut_for_search(text) # 结巴分词,搜索引擎模式,召回率更高
        text = " ".join(text_seg_list) # 用空格连接词语

        # 给标题分词
        title_seg_list = jieba.cut(title, cut_all=False) # 结巴分词,精确模式,更可读
        title = " ".join(title_seg_list) # 用空格连接词语

        self._entries.append({
            'title': title,
            'text': str(text.encode('utf-8'), encoding='utf-8'),
            'location': loc
        })

    # 以下不变,略

实际效果参见我的博客,如下:

也可以把search组件复制、更改为自己的插件,参考插件fastsearch

如果要更改搜索引擎的行为,比如给输入的搜索内容进行分词、排除停用词等,可参考lunr的日语组件

mkdocs-macros-plugin宏插件#

通过变量和宏释放Mkdocs的威力。号称:

Note

This is more than a plugin: it's a mini-framework!

似乎比较慢。不如用插件直接替换markdown锚点。

部署到GitHub#

参见GitHub Pages官方文档

评论