本文既是 matery 主题内容 tag 插件的完整使用教程,也是 自动化测试靶场。主题原生注册的 tag 见
themes/matery/scripts/tags/(index.js 统一注册)。自动化测试会断言下方标注的渲染标记。
1. 便签 note
用途:文章内的彩色提示块 —— 提醒、警告、成功反馈、补充说明。
学习路径:最小示例 → 六种颜色 → 隐藏图标 → 可折叠 → 嵌套组件 → 完整参数 → 边界与坑
1.1 最小示例
效果(页面实际渲染):
这是一条最简提示。结构就是:开始标签 + 内容 + 结束标签。
对应源码:
{% note %}
这是一条最简提示。结构就是:开始标签 + 内容 + 结束标签。
{% endnote %}不写任何颜色关键字时,得到灰边框的普通提示块。
1.2 六种颜色
效果:
default — 普通提示
primary — 重点强调
info — 补充说明
success — 操作成功
warning — 注意事项
danger — 严重警告
对应源码:
{% note default %}**default** — 普通提示{% endnote %}
{% note primary %}**primary** — 重点强调{% endnote %}
{% note info %}**info** — 补充说明{% endnote %}
{% note success %}**success** — 操作成功{% endnote %}
{% note warning %}**warning** — 注意事项{% endnote %}
{% note danger %}**danger** — 严重警告{% endnote %}颜色对照表:
| 关键字 | 图标(Font Awesome 码) | 语义 | 底色来源 |
|---|---|---|---|
default | f0a9(右箭头) | 普通提示 | --text-color 8% 混合 |
primary | f055(加号) | 重点强调 | --tagsP-purple-color 12% |
info | f05a(信息) | 补充说明 | --info 8% |
success | f058(对勾) | 操作成功 | --success 8% |
warning | f06a(感叹) | 注意事项 | --warning 8% |
danger | f056(叉) | 严重警告 | --danger 8% |
底色用
color-mix()与卡片底色混合,所以明暗模式自动适配,无需为暗色单独写一套。
1.3 隐藏图标(no-icon)
效果:
对应源码:
{% note warning no-icon %}没有左侧图标,但保留警告色边框。{% endnote %}位置可互换(两个都是关键字,顺序无关):
{% note no-icon warning %}与上一例完全等价{% endnote %}
{% note danger no-icon %}与上一例完全等价{% endnote %}1.4 可折叠(details)
在关键字之后写标题,就会生成可折叠块(<details><summary>):
效果:
点击展开:静态站为什么够用
静态站没有服务端,但用 构建时快照 可以获得大部分动态能力。
代价
- 内容更新需重新构建
- 搜索是本地索引,不是全文检索服务
对应源码:
{% note info 点击展开:静态站为什么够用 %}
静态站没有服务端,但用 **构建时快照** 可以获得大部分动态能力。
### 代价
- 内容更新需重新构建
- 搜索是本地索引,不是全文检索服务
{% endnote %}标题和正文都走 Markdown 渲染 —— 标题里可以加粗、正文里可以有列表/代码/小标题。
1.5 嵌套其它组件
正文走 Markdown 渲染,所以内部可以再放任何 tag:
效果:
组合示例
这一块内部放了 标签 和 粗体,还有代码 console.log(1)。
二级便签
便签可以嵌套便签,内层用内层的颜色。
对应源码:
{% note success 组合示例 %}
这一块内部放了 {% label primary@标签 %} 和 **粗体**,还有代码 `console.log(1)`。
{% note warning 二级便签 %}
便签可以嵌套便签,内层用内层的颜色。
{% endnote %}
{% endnote %}1.6 完整参数
参数按空格切分。开头最多取 2 个关键字作 CSS 类,剩余参数全部拼成折叠标题。
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 颜色关键字 | ❌ | 无 | default / primary / info / success / warning / danger |
| 2 | no-icon | ❌ | 无 | 隐藏左侧图标;与颜色位置可互换 |
| 2+ | 折叠标题 | ❌ | 无 | 非关键字参数全部合并为 <summary>,走 Markdown |
| 内容 | 正文 | ❌ | 空 | 走 Markdown,可嵌套其它 tag |
全部可选值:
| 关键字 | 类型 | 说明 |
|---|---|---|
default | 颜色 | 灰 |
primary | 颜色 | 紫 |
info | 颜色 | 蓝 |
success | 颜色 | 绿 |
warning | 颜色 | 橙 |
danger | 颜色 | 红 |
no-icon | 修饰符 | 不是颜色,只隐藏图标 |
别名:subnote 与 note 注册的是同一个函数,行为与样式完全相同(不是"次级")。
1.7 边界与坑
| 情况 | 行为 |
|---|---|
首词不是关键字(如 提示内容) | 不加任何颜色类,该词变折叠标题 → 得到无颜色可折叠块,不是普通块 |
| 写 3 个颜色关键字 | 只取前 2 个 → class="note info success",第 3 个变折叠标题 |
颜色写错(Warning 大写) | 大小写敏感 → 视为非关键字,变折叠标题且无颜色 |
| 内容为空 | 输出空 div,不报错 |
| 忘记结束标签 | 构建报 unclosed tag |
| 想普通块但写了标题 | 做不到 —— 有非关键字参数就会折叠。把文字挪到正文里 |
与 ::: 容器混用 | 产物不同:tag 是 <div class="note primary">,容器是 <div class="note">(CSS 已用属性选择器区分) |
1.8 小结
- 最常用:
note info/note warning两三个词就够 - 想折叠:在颜色后直接写标题
- 想纯色块无图标:加
no-icon - 想嵌套:正文里继续写 tag(记得成对闭合)
2. 时间线 timeline
用途:按时间顺序展示版本记录、项目里程碑、个人成长历程 —— 竖线 + 圆点串起一串带日期的节点。
学习路径:最小示例 → 颜色与无标题 → 完整参数 → 进阶 → 边界与坑
2.1 最小示例
一个标题 + 一个节点,就是完整的时间线。
效果(页面实际渲染):
里程碑
2026-08
- 新增内容 tag 测试页
对应源码:
{% timeline 里程碑,green %}
<!-- timeline 2026-08 -->
- 新增内容 tag 测试页
<!-- endtimeline -->
{% endtimeline %}2.2 常用变体
变体 1:不要 headline(完全不给参数;注意不要只写标题,否则会渲染出 class="timeline undefined")
效果:
2026-07
- 只有节点,没有整体标题
对应源码:
{% timeline %}
<!-- timeline 2026-07 -->
- 只有节点,没有整体标题
<!-- endtimeline -->
{% endtimeline %}变体 2:换颜色(blue / pink / red / purple / orange / green)
效果:
蓝色主题
2026-01
- 蓝
红色主题
2026-02
- 红
对应源码:
{% timeline 蓝色主题,blue %}
<!-- timeline 2026-01 -->
- 蓝
<!-- endtimeline -->
{% endtimeline %}
{% timeline 红色主题,red %}
<!-- timeline 2026-02 -->
- 红
<!-- endtimeline -->
{% endtimeline %}2.3 完整参数
参数先空格 join 再 split(','),因此只有两个位置参数。
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 标题 | ❌ | 无 | 整体 headline,Markdown 渲染;省略则无 headline |
| 2 | 颜色 | ❌ | 无 | 直接拼进 class 名(不校验合法性) |
| 子块 | <!-- timeline 标题 --> … <!-- endtimeline --> | ❌ | — | 每个事件节点,标题与正文均走 Markdown |
全部可选值(6 色)
| 颜色 | 竖线 / 圆点色 |
|---|---|
blue | 蓝 |
pink | 粉 |
red | 红 |
purple | 紫 |
orange | 橙 |
green | 绿 |
| 省略 | 用 .timeline 默认色(不追加颜色类) |
2.4 进阶:节点里的 Markdown 与其它 tag
节点标题和正文都走 Markdown 渲染,所以能放粗体、链接,甚至嵌 {% label %}。
效果:
主题功能演进记录
2026-08
- 新增内容 tag 测试页(note/timeline/tabs/label 等完整教程)
- 相册数据重构(galleries.yml 三种形式)
- 统一 lightGallery 图像查看库 新增
2026-07
- 评论区架构优化(comment: waline) 优化
- 修复 href=/ MIME 样式错误 修复
2020
- 创建博客,开始 matery 深度定制
对应源码:
{% timeline 主题功能演进记录,green %}
<!-- timeline 2026-08 -->
- 新增内容 tag 测试页(note/timeline/tabs/label 等完整教程)
- 相册数据重构(galleries.yml 三种形式)
- 统一 lightGallery 图像查看库 {% label success@新增 %}
<!-- endtimeline -->
<!-- timeline 2026-07 -->
- 评论区架构优化(comment: waline) {% label info@优化 %}
- 修复 href=/ MIME 样式错误 {% label warning@修复 %}
<!-- endtimeline -->
<!-- timeline 2020 -->
- 创建博客,开始 matery 深度定制
<!-- endtimeline -->
{% endtimeline %}2.5 边界与坑
| 情况 | 行为 |
|---|---|
| 只写标题、不写颜色 | 输出 class="timeline undefined" → 无匹配类,回退默认色(建议颜色必写) |
颜色拼错(如 grey) | 类名照写但无 CSS → 回退默认色,不报错 |
<!--timeline 标题-->(timeline 后无空格) | 正则不匹配 → 该节点连同内容整体消失 |
--> 后不换行 | 开标签要求 -->\n,不匹配 → 同上 |
| 标题里含英文逗号 | 被 split(',') 截断,逗号后内容丢失 |
| 完全不给参数 | 无 headline、颜色类为空,只渲染子节点 |
没有任何 <!-- timeline --> 子块 | 只渲染 headline(或空容器) |
颜色名写两个单词(light blue) | 被空格拆开,只保留第一个词,其余丢弃 |
2.6 小结
- 最常用:
{% timeline 标题,颜色 %}+ 若干<!-- timeline 日期 --> … <!-- endtimeline --> - 不想要 headline:一个参数都别写(只写标题会得到
undefined类名) - 换主题色:第 2 个参数写颜色名,写错不报错、只回退默认色
- 节点能放什么:Markdown 全支持,可以嵌
label/button等内联 tag
测试断言:页面含
timeline标记 + 3 个时间节点。
3. 标签页 tabs
用途:一块区域放多份内容,点击标题切换 —— 适合对比、分步骤、多方案并列展示。
学习路径:最小示例 → 激活序号 / 图标 → 完整参数 → 进阶 → 边界与坑
3.1 最小示例
两个标签,默认激活第一个。
效果(页面实际渲染):
这里是第一个标签的内容。
对应源码:
{% tabs 最小示例组,1 %}
<!-- tab 第一个 -->
这里是第一个标签的内容。
<!-- endtab -->
<!-- tab 第二个 -->
这里是第二个标签的内容。
<!-- endtab %}
{% endtabs %}3.2 常用变体
变体 1:默认激活第二个(第 2 个参数从 1 起算)
效果:
打开时不是它。
打开时默认显示这一页。
对应源码:
{% tabs 激活第二项,2 %}
<!-- tab 第一个 -->
打开时不是它。
<!-- endtab -->
<!-- tab 第二个 -->
打开时默认显示这一页。
<!-- endtab %}
{% endtabs %}变体 2:给标题加图标(标题 @类型:图标名)
效果:
- 功能全面
- 配置灵活
- 配置项多,有学习成本
对应源码:
{% tabs 带图标,1 %}
<!-- tab 优点 @fas:star -->
- 功能全面
- 配置灵活
<!-- endtab -->
<!-- tab 缺点 @fas:exclamation-triangle -->
- 配置项多,有学习成本
<!-- endtab -->
<!-- tab 关于 @fab:github -->
- 源码在 GitHub
<!-- endtab %}
{% endtabs %}3.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 组名 | ✅ | — | 生成容器 DOM id(转小写、空格→-);同页必须全局唯一 |
| 2 | 激活序号 | ❌ | 0(= 第一个激活) | 1 起算;写 0 或省略 = 第一个激活;超范围 = 全部不激活 |
| 子块 | <!-- tab 标题 @类型:图标名 --> … <!-- endtab --> | ❌ | 自动命名 | 每个标签;tab 后必须有空格 |
| 内容 | 标签正文 | ❌ | 空 | 走 Markdown 渲染,可嵌套内联 tag |
图标前缀(@ 之后写 类型:图标名)
| 写法 | 实际 class |
|---|---|
fas:home / solid:home | fas fa-home |
far:home / regular:home | far fa-home |
fal:home / light:home | fal fa-home |
fad:home / duotone:home | fad fa-home |
fab:github | fab fa-github |
未知类型(如 xxx:home) | 回退 far |
| 不写图标 | 不输出 <i> |
3.4 进阶:标签内容承载完整 Markdown 块
效果:
- 功能全面:内容 tag 覆盖 10+ 插件
- 配置灵活:每个功能可独立开关
- 性能优化:懒加载 + 占位防 CLS
// 内容里照样能放代码块
const tag = 'tabs'- 配置项多,上手有学习成本
- 部分功能依赖外部 CDN
- 技术博客:代码 / 教程 / 对比
- 个人博客:相册 / 项目 / 历程
对应源码:
{% tabs 主题功能对比,1 %}
<!-- tab 优点 -->
- 功能全面:内容 tag 覆盖 10+ 插件
- 配置灵活:每个功能可独立开关
- 性能优化:懒加载 + 占位防 CLS
```js
// 内容里照样能放代码块
const tag = 'tabs'
```
<!-- endtab -->
<!-- tab 缺点 -->
- 配置项多,上手有学习成本
- 部分功能依赖外部 CDN
<!-- endtab -->
<!-- tab 适用场景 -->
- 技术博客:代码 / 教程 / 对比
- 个人博客:相册 / 项目 / 历程
<!-- endtab -->
{% endtabs %}3.5 边界与坑
| 情况 | 行为 |
|---|---|
| 缺组名 | log.error("[Tabs] Missing required tab name") → 返回空串,整块消失 |
| 同页两个同名组 | DOM id 冲突,点一组会连带切换另一组 |
| 激活序号 > tab 数(或负数) | 没有一个 tab 带 active → 首屏空白 |
激活序号写 0 | 等价省略(第一个激活) |
<!--tab 标题-->(tab 后无空格) | 正则不匹配 → 该 tab 及内容丢失 |
| 标题与图标都为空 | 自动用「组名 + 序号」作标题 |
| 组名含中文/特殊字符 | id 原样保留(HTML5 合法,CSS 选择器需转义) |
配置 tag_tabs.cdn / tag_tabs.layout | cdn 默认 https://fastly.jsdelivr.net/npm/;layout 默认 ['post','page'],当前渲染逻辑未实际读取(仅兼容保留) |
3.6 小结
- 最常用:
{% tabs 组名 %}里塞若干<!-- tab 标题 --> … <!-- endtab --> - 想默认展开第 N 个:第 2 个参数写 N(1 起算,
0/省略都是第一个) - 想加图标:标题后加
@fas:star(类型:名字) - 组名必须全页唯一,否则点击会串组
测试断言:页面含
nav-tabs标记 + 3 个标签。
4. 行内标签 label
用途:正文里的小色块,给关键词、状态、分类打标记 —— 不打断句子节奏。
学习路径:最小示例 → 六色 → 完整参数 → 进阶 → 边界与坑
4.1 最小示例
效果(页面实际渲染):
主要对应源码:
{% label primary@主要 %}★ 参数顺序反直觉:颜色在
@前,文字在@后(源码split('@'),classes=args[0]、text=args[1])。
4.2 常用变体:六种颜色
效果:
默认 主要 成功 信息 警告 危险对应源码:
{% label default@默认 %} {% label primary@主要 %} {% label success@成功 %} {% label info@信息 %} {% label warning@警告 %} {% label danger@危险 %}4.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 类型 | ❌ | default | 6 种语义色之一,拼成 label-<类型> 类名 |
| 2 | 文字 | ✅ | 空 | 标签文案;为空时 log.warn 并输出空标签 |
全部可选值(6 色)
| 类型 | 语义 |
|---|---|
default | 默认灰 |
primary | 主色(紫/主题色) |
success | 成功(绿) |
info | 信息(蓝) |
warning | 警告(橙) |
danger | 危险(红) |
4.4 进阶:嵌进句子表达状态
效果:
部署状态:已发布 灰度状态:待观察 回滚状态:已下线 —— 标签与文字混排时不换行、不破坏行高。
对应源码:
部署状态:{% label success@已发布 %} 灰度状态:{% label warning@待观察 %} 回滚状态:{% label danger@已下线 %} —— 标签与文字混排时不换行、不破坏行高。4.5 边界与坑
| 情况 | 行为 |
|---|---|
| 只写类型不写文字 | log.warn("Label text must be defined!") → 输出空 <span> |
完全没有 @ | 整串被当作类型,文字为空 → 空标签 + warn |
文字里含 @ | 只保留第一个 @ 与第二个 @ 之间的内容,其余丢弃 |
类型拼错 / 写中文(如 成功) | 类名照写(label-成功),CSS 无匹配 → 无样式 |
文字含 Markdown(**粗**) | 不渲染,原样显示星号 |
| 文字含 HTML | 原样插入(源码不转义),可放 <b> 等内联标签 |
| 同一行多个 label | 空格分隔即可,各自独立 |
4.6 小结
- 最常用:
{% label success@完成 %} - 记顺序:颜色
@文字(写成文字@颜色会得到无样式标签) - 想多色并排:一行写多个,空格分隔
- 别指望 Markdown:文字原样输出,要粗体就自己写
<b>
测试断言:页面含
class="label标记(6 种颜色)。
5. 按钮 button
用途:文章里的行动入口 —— 跳转、下载、引导读者下一步。
学习路径:最小示例 → 四种写法 → 完整参数 → 进阶 → 边界与坑
5.1 最小示例
效果(页面实际渲染):
返回首页对应源码:
{% button /,返回首页,home,首页 %}★ 链接是第一个参数(源码
url=args[0]、text=args[1]),顺序写反按钮就指向文字。
5.2 常用变体
效果:
访问GitHub 关于我 纯文字按钮 Hexo对应源码:
{% button https://github.com/appotry,访问GitHub,github,GitHub %}
{% btn /about,关于我,user-circle,关于 %}
{% button /tags,纯文字按钮,,标签 %}
{% button https://hexo.io,Hexo,fa fa-book,官网 %}四个变体分别是:带图标的标准按钮、别名 btn、纯文字按钮(图标留空)、图标写完整 fa fa-xxx 类名。
5.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 链接 | ✅ | 空 | URL / 相对路径;为空时 warn,仍输出 href="" |
| 2 | 文字 | ❌ | 空 | 按钮文案,不渲染 Markdown |
| 3 | 图标 | ❌ | 空 | FA 类名;不以 fa 开头时自动补 fa fa- |
| 4 | title | ❌ | 空 | hover 提示;为空则不输出该属性 |
| 5+ | — | ❌ | — | 静默忽略 |
别名:{% btn %} 与 {% button %} 注册的是同一个函数,完全等价。
5.4 进阶:按钮放进便签里做引导
效果:
对应源码:
{% note info 下一步 %}
读完这篇建议继续看布局篇,或者直接去标签页找同类文章:
{% button /posts/8a11fe0a/,内容 tag 篇,file-text,当前页 %}
{% button /tags,按标签浏览,tags,标签云 %}
{% endnote %}5.5 边界与坑
| 情况 | 行为 |
|---|---|
| 任何参数含英文逗号 | 先 join(' ') 再 split(',') → 参数被切断,后续全部错位 |
图标写 home | 输出 <i class="fa fa-home"> |
图标写 fas fa-home / fa-home | 以 fa 开头 → 原样保留(不重复加前缀) |
| 缺链接 | log.warn("URL can NOT be empty.") → 仍渲染 <a href="">(点击回当前页) |
| 第 5 个及以后参数 | 静默丢弃 |
| title 为空 | 不输出 title="" 属性 |
| 文字留空但给了图标 | 只有图标的按钮(纯图标按钮可用) |
别名 btn | 与 button 完全等价(同一函数注册) |
5.6 小结
- 最常用:
{% button 链接,文字,图标,title %} - 记顺序:链接第一、文字第二(写反就跳错地方)
- 纯文字按钮:图标位留空但逗号保留
- 参数别带逗号:任何逗号都会把后面的参数切错位
测试断言:页面含
btn标记(≥4 个按钮)。
6. GitHub 卡片 githubCard
用途:把仓库变成一张能点的卡片(星标 / 分支 / 简介),比裸链接更有说服力。
学习路径:最小示例 → 用户卡与尺寸 → 完整参数 → 进阶 → 边界与坑
6.1 最小示例
效果(页面实际渲染):
对应源码:
{% githubCard user:appotry repo:hexo %}参数是
key:value(冒号),空格分隔 —— 写成width=300会被整条忽略。
6.2 常用变体
变体 1:只给 user(渲染用户卡,仓库区为空)
效果:
对应源码:
{% githubCard user:hexojs repo:hexo %}变体 2:指定尺寸
效果:
对应源码:
{% githubCard user:appotry repo:hexo-theme-matery width:400 height:140 %}6.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 任意 | user:名称 | ✅ | — | GitHub 用户名;缺失时输出红字错误块 |
| 任意 | repo:仓库 | ❌ | — | 仓库名;省略则显示用户卡片 |
| 任意 | width:300 / width:300px | ❌ | 组件自带 | 自动去掉尾部 px |
| 任意 | height:150 / height:150px | ❌ | 组件自带 | 同上 |
| 任意 | theme:主题 | ❌ | default | 值为 default 时不输出该属性 |
| 任意 | target:目标 | ❌ | blank | 值为 blank 时不输出该属性 |
| 任意 | client_id:xxx | ❌ | — | OAuth client_id,用于提升 API 限额 |
| 任意 | client_secret:xxx | ❌ | — | 不支持(会泄漏到前端 HTML,源码刻意不实现) |
6.4 进阶:并列展示两个仓库
效果:
对应源码:
{% githubCard user:appotry repo:hexo %}
{% githubCard user:hexojs repo:hexo %}两个卡片之间有 Markdown 空行,各自独立成块。
6.5 边界与坑
| 情况 | 行为 |
|---|---|
缺 user | 输出 <div style="color:red">GitHub Card配置错误:缺少user参数</div>,构建不失败 |
只给 user 不给 repo | 渲染用户卡片(仓库区为空) |
| 值里含空格 | 被空格切成两个 token,第二个无冒号 → 丢弃 |
写 width=300(等号) | 找不到冒号 → 参数被忽略 |
theme:default / target:blank | 属性不输出,走组件默认值 |
| 页面禁用 JS | <noscript> 显示 “JavaScript is required to load GitHub Cards” |
| 卡片加载前 | 容器预留 min-height: 150px,防止布局抖动 |
| 同名参数写两次 | 后者覆盖前者(按 key 存对象) |
6.6 小结
- 最常用:
{% githubCard user:xxx repo:yyy %} - 只要用户卡:省掉
repo - 改尺寸:
width:400 height:140(px可带可不带) - 提升 API 限额:加
client_id;永远不要写client_secret
测试断言:页面含
github-card或仓库名链接标记。
7. Mermaid 流程图
用途:把流程、时序、类关系画成图 —— 用文本描述,构建时渲染成 SVG。
学习路径:最小示例 → 三种常用图 → 完整参数 → 进阶 → 边界与坑
⚠️ 前置条件:文章 front-matter 必须有
mermaid: true,且主题post.mermaid.enable: true。缺 front-matter 时整段脚本不输出,图静默空白(本文已声明)。
7.1 最小示例
效果(页面实际渲染):
graph TD
A --> B
对应源码:
{% mermaid %}
graph TD
A --> B
{% endmermaid %}7.2 常用变体:三种最常用的图
变体 1:流程图
效果:
graph TD
A[构建] --> B{测试}
B -->|通过| C[部署]
B -->|失败| D[修复]
D --> B
C --> E[监控]
对应源码:
{% mermaid %}
graph TD
A[构建] --> B{测试}
B -->|通过| C[部署]
B -->|失败| D[修复]
D --> B
C --> E[监控]
{% endmermaid %}变体 2:时序图
效果:
sequenceDiagram
participant U as 用户
participant S as 服务器
participant D as 数据库
U->>S: 发起请求
S->>D: 查询数据
D-->>S: 返回结果
S-->>U: 响应页面
对应源码:
{% mermaid %}
sequenceDiagram
participant U as 用户
participant S as 服务器
participant D as 数据库
U->>S: 发起请求
S->>D: 查询数据
D-->>S: 返回结果
S-->>U: 响应页面
{% endmermaid %}变体 3:类图
效果:
classDiagram
class 用户 {
+String 姓名
+登录()
}
class 博客 {
+String 标题
+发布()
}
用户 --> 博客
对应源码:
{% mermaid %}
classDiagram
class 用户 {
+String 姓名
+登录()
}
class 博客 {
+String 标题
+发布()
}
用户 --> 博客
{% endmermaid %}7.3 完整参数
tag 参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 任意 | 任意文本 | ❌ | 空 | 不是 Mermaid 配置——全部原样拼进 <code class="mermaid" …> 的 HTML 属性位 |
也就是说写
theme=dark只是给<code>加了个 HTML 属性,不会改变图表主题;主题走下面的post.mermaid.options.theme。
配置项(主题 _config.yml 的 post.mermaid)
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
enable | true / false | — | 总开关 |
specific | true / false | true | true 时仅 mermaid: true 的文章输出脚本 |
options.theme | default / forest / dark / neutral / base | default | 图表主题 |
options.startOnLoad | true / false | false | 主题自行控制渲染时序,不要改 true |
options.securityLevel | strict / loose / antiscript / sandbox | loose | HTML 标签放行程度 |
options.flowchart.useMaxWidth | true / false | true | 流程图是否撑满容器 |
支持的图表类型:graph / flowchart、sequenceDiagram、classDiagram、stateDiagram-v2、gantt、pie、erDiagram、gitGraph、journey、mindmap、timeline、quadrantChart。
7.4 进阶:饼图(稳定输出,适合占比)
效果:
pie title 内容类型占比
"教程" : 45
"排障" : 30
"随笔" : 25
对应源码:
{% mermaid %}
pie title 内容类型占比
"教程" : 45
"排障" : 30
"随笔" : 25
{% endmermaid %}7.5 边界与坑
| 情况 | 行为 |
|---|---|
specific: true 但 front-matter 没写 mermaid: true | 主题整段脚本不输出 → 图空白,控制台无报错(最隐蔽的坑) |
在 tag 里写 theme=dark | 只加 HTML 属性,图表主题不变 |
| 图语法错误(缩进/箭头写错) | Mermaid 抛 parse error,容器留空并显示错误图 |
| 正文用 mermaid 代码围栏(三个反引号) | 不会自动渲染(属于另一条前端插件路径),请统一用本 tag |
startOnLoad: true | 与主题自行渲染的时序冲突,可能双渲染/首屏闪烁 |
| 同页多张图 | 各自独立渲染,互不影响 |
| 加密片段内的图 | 解密后由 events.refresh() 重渲染(幂等) |
| 本页 front-matter | 已设 mermaid: true,是上方示例能渲染的前提 |
7.6 小结
- 最常用:
{% mermaid %}里贴原生 Mermaid 语法 - 记前提:front-matter 必须有
mermaid: true(specific: true时) - 换主题:改配置
post.mermaid.options.theme,不是写在 tag 参数里 - 别用代码围栏:三个反引号的 mermaid 块不渲染,统一用 tag
- 语法错了不报构建:只是那块空白,看控制台 parse error
测试断言:页面含
class="mermaid"标记(≥3 个图表)。
8. PDF 嵌入 pdf
用途:文章里直接翻 PDF —— 手册、图纸、报告不用跳出去。
学习路径:最小示例 → 完整参数 → 进阶 → 边界与坑
8.1 最小示例
效果(页面实际渲染):
对应源码:
{% pdf /doc/Linux学习笔记.pdf %}8.2 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | URL | ✅ | — | PDF 地址;没有 width/height 等其它参数 |
类型自动检测(按顺序匹配,先命中先返回)
| 顺序 | URL 特征 | 类型 | 渲染方式 |
|---|---|---|---|
| 1 | 含 .pdf | normal | <embed>,高 550px |
| 2 | 含 drive.google.com | googledoc | <iframe>,高 550px |
| 3 | 含 www.slideshare.net | slideshare | <iframe>,高 550px |
| 4 | 其它 | normal | <embed> |
8.3 进阶:前面加一句说明(便签 + PDF 组合)
效果:
移动端提示
手机浏览器大多不支持内联 PDF,会退化成下载或空白 —— 桌面端体验完整。
对应源码:
{% note warning 移动端提示 %}
手机浏览器大多不支持内联 PDF,会退化成下载或空白 —— 桌面端体验完整。
{% endnote %}
{% pdf /doc/Linux学习笔记.pdf %}8.4 边界与坑
| 情况 | 行为 |
|---|---|
Google Drive 直链(URL 含 .pdf) | 先命中第 1 条 → 按 normal embed,不会走 Google 预览 |
drive.google.com/viewer?url=xxx.pdf | 同样含 .pdf → 被判为 normal |
slideshare 链接里带 .pdf | 被判为 normal |
| 想改高度 | 无参数可调,源码 3 处硬编码 550px,只能改源码或额外 CSS 覆盖 |
| 空参数 | 输出 <embed src="">,空白容器 |
| 相对路径 | 以站点根为基准(如 /medias_webp/docs/sample.pdf) |
| 移动端浏览器 | 多数不支持内联 PDF → 空白或提示下载(非 tag 问题) |
8.5 小结
- 最常用:
{% pdf /path/file.pdf %} - 记限制:只有一个参数,高度写死 550px
- 想走 Google 预览:URL 里不能出现
.pdf,否则被判为普通 embed - 站内文件:以站点根为起点写路径
测试断言:页面含 pdf 相关容器标记。
9. 插入片段 insertmd
用途:把
source/_template/里的 Markdown 片段插进正文 —— 一处维护、多处复用(系列目录、免责声明、广告位)。学习路径:最小示例 → 带分隔符 → 完整参数 → 边界与坑
9.1 最小示例
效果(页面实际渲染):
系列教程
对应源码:
{% insertmd '_template/系列教程.md' %}9.2 常用变体:加分隔符
第 2 个参数是 Markdown 分隔内容,会追加在每个文件之后(含最后一个)。
效果:
系列教程
对应源码:
{% insertmd '_template/系列教程.md' '---' %}9.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 路径 | ✅ | — | 相对 source/(不是相对当前文章);文件或目录均可 |
| 2 | 分隔符 | ❌ | 空 | Markdown 渲染后追加在每个文件之后(含最后一个) |
行为细则
| 项 | 说明 |
|---|---|
| 目录模式 | 传目录则递归收集所有 .md(其它扩展名忽略),一次插入整目录 |
| 排序 | 按完整路径 localeCompare → 第10篇 排在 第2篇 前面(字典序) |
| 分隔符 | 每个文件渲染后都追加一次,末尾也会多一个 |
| 渲染器 | 自动识别 markdown-it / marked;以 disableNunjucks: true 渲染 → 片段内的 Hexo tag 语法不会执行 |
| 注册方式 | { async: true, ends: false, priority: 2 } |
| 依赖 | fs-extra |
9.4 边界与坑
| 情况 | 行为 |
|---|---|
| 路径不存在 | log.error + 返回空串 → 页面静默缺内容,构建不失败 |
路径写成相对文章(../) | 解析基准是 source/,会找不到(静默空) |
| 分隔符含空格 | 需引号包裹,否则只取到前两段 |
| 目录层级过深/文件过多 | 无深度限制,大目录会明显拖慢构建 |
| 片段内嵌 tag | 不执行(disableNunjucks),只按纯 Markdown 渲染 |
| 单个文件渲染异常 | 该文件返回空串,其它文件继续 |
9.5 小结
- 最常用:
{% insertmd '_template/xxx.md' %}(路径相对source/) - 想加分隔线:第 2 参数写
'---' - 想批量插:传目录,按路径字典序合并
- 片段里别写 tag:
disableNunjucks下不执行,只当纯文本
测试断言:渲染后含模板片段内容。
10. 微信对话卡片 wechat_dialog
用途:把问答、客服对话、报错排查过程变成聊天截图的样子 —— 比正文引用更直观。
学习路径:最小示例 → 多轮 + Markdown → 完整参数 → 进阶 → 边界与坑
10.1 最小示例
效果(页面实际渲染):
对应源码:
{% wechat_dialog %}
User: 你好
Assistant: 你好,有什么可以帮您?
{% endwechat_dialog %}★ 必须是
User:/Assistant:行格式,不是行内参数 —— 写{% wd 文字,发送者 %}会渲染异常。
10.2 常用变体:多轮且带 Markdown
效果:
对应源码:
{% wechat_dialog %}
User: 你好,我想了解 matery 主题
Assistant: 好的,有什么可以帮您?
User: 主题支持哪些功能?
Assistant: 内容 tag、相册、搜索、评论等 **20+** 功能
Assistant: 常用清单:
- 内容 tag(note / tabs / timeline)
- 相册与图库
- 本地搜索 + Pagefind
{% endwechat_dialog %}10.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| — | 无 | — | — | 不接受位置参数,写了也会被忽略;内容全部来自块内的角色行 |
块内规则
| 写法 | 行为 |
|---|---|
User: 开头 | 新消息:右侧、绿色气泡、头像 2.jpg |
Assistant: 开头 | 新消息:左侧、灰色气泡、头像 1.jpg |
| 其它行 | 追加到当前消息(保留换行) |
| 消息正文 | 用独立 new MarkdownIt() 渲染(不走 Hexo 渲染器) |
10.4 进阶:别名 wd 写排障对话
效果:
对应源码:
{% wd %}
User: 改了样式为什么不生效?
Assistant: 先确认是不是缓存:**主题配置改动必须重新 clean 构建**。
User: clean 了还是不变
Assistant: 那看两点:
1. 配置改的是 `userConfig/_config.tmp.yml` 还是构建产物 `themes/matery/_config.yml`
2. 容器里 `pm2 restart` 不够,`db.json` 缓存还在
{% endwd %}10.5 边界与坑
| 情况 | 行为 |
|---|---|
中文冒号 User: | 不识别(正则要求英文 :)→ 整行并入上一条消息 |
小写 user: / assistant: | 不识别(大小写敏感)→ 并入上一条 |
| 第一条角色行之前有内容 | currentRole 为空 → 按 assistant 气泡输出 |
| 整块没有角色行 | 全部内容合成一条 assistant 消息 |
消息里写 {% note %} 等 tag | 不生效(不经过 Hexo tag 管线) |
| 头像 | 硬编码 /medias_webp/avatar/1.jpg、2.jpg,换头像需改源码 |
| 空块 | 只输出空 .chat-container |
10.6 小结
- 最常用:每行以
User:/Assistant:开头,其余行自动归入当前消息 - 别名:
{% wd %}完全等价 - 能放 Markdown:粗体、列表、行内代码都行
- 不能放 tag:块内
{% note %}之类不执行 - 冒号必须英文,大小写必须严格
测试断言:页面含微信对话容器标记(
.chat-container)。
11. 分组图片 groupimage
用途:多张图片按行列网格排列 —— 相册缩略、对比图、步骤截图不再一张一张竖着堆。
学习路径:最小示例 → 默认布局 → 完整参数 → 进阶 → 边界与坑
11.1 最小示例
3 张图,2-1 布局(第一行 2 张、第二行 1 张)。
效果(页面实际渲染):



对应源码:
{% groupimage 3 2-1 %}



{% endgroupimage %}11.2 常用变体:省略布局
不给第 2 个参数时按总数取默认布局(4 张 → 2-2)。
效果:




对应源码:
{% groupimage 4 %}




{% endgroupimage %}11.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 总数 | ✅ | — | parseInt 解析;决定布局与每行配额 |
| 2 | 布局 | ❌ | 见下表 | - 分隔的每行张数(3-2 = 3 张 + 2 张) |
| 内容 | 图片 | ✅ | — | 标准 ![]();从渲染后 HTML 的 <img> 中提取 |
默认布局(未给布局或校验失败时)
| 总数 | 布局 | 总数 | 布局 |
|---|---|---|---|
| 2 | 1-1 | 7 | 3-2-2 |
| 3 | 2-1 | 8 | 3-2-3 |
| 4 | 2-2 | 9 | 3-3-3 |
| 5 | 3-2 | 10 | 3-2-2-3 |
| 6 | 3-3 | >10 | 每行 3 张 |
别名:{% gi %} 与 {% groupimage %} 注册的是同一个函数,完全等价。
11.4 进阶:自定义三行布局
效果:





对应源码:
{% gi 5 3-1-1 %}





{% endgi %}11.5 边界与坑
| 情况 | 行为 |
|---|---|
sum(布局) != 总数 | 静默回退默认布局(不报错、不提示) |
| 图片数 < 总数 | 后面的行分到空数组 → 出现空行/留白 |
| 图片数 > 总数 | 超出配额的图片被丢弃 |
| 总数 = 1 或 > 10 且未给布局 | 回退「每行 3 张」 |
布局写 3,2(逗号) | 只按 - 切分,3,2 解析为 3 → sum 不符 → 回退默认 |
| 图片写成裸 URL / 其它语法 | 提取不到 <img> → 该图不显示(只认渲染后的 <img>) |
| 总数写非数字 | parseInt → NaN → 无默认布局 → 每行 3 张 |
11.6 小结
- 最常用:
{% groupimage 数量 布局 %}...{% endgroupimage %} - 不想算布局:只写数量,用默认布局
- 自定义:
3-2-1这种-分隔写法,各段之和必须等于数量,否则静默回退 - 图片必须用 Markdown 语法:裸 URL 提取不到
测试断言:页面含
group-image容器标记。
12. URL 卡片 cardurl
用途:把一个链接做成带标题/描述的卡片 —— 推荐资源、引用官方文档时比裸链接体面。
学习路径:最小示例 → 带图标 → 完整参数 → 进阶 → 边界与坑
12.1 最小示例
效果(页面实际渲染):
对应源码:
{% cardurl [url=https://github.com/appotry/hexo] [title=夜法之书-hexo仓库] [desc=个人独立blog源码,基于hexo搭建] %}参数必须带方括号(
[key=value])。漏掉方括号 → 正则不匹配 → 参数被静默忽略,得到一张空卡片。
12.2 常用变体:加左侧图标
效果:
对应源码:
{% cardurl [url=https://blog.17lai.site] [title=夜法之书] [desc=个人独立技术博客] [avatar=/medias_webp/avatar/1.jpg] %}12.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 任意 | [url=...] | ✅ | — | 目标链接 |
| 任意 | [title=...] | ❌ | 同 url | 卡片标题 |
| 任意 | [desc=...] | ❌ | 无 | 描述文字 |
| 任意 | [avatar=...] | ❌ | 无 | 左侧图标;省略则不输出 <img> |
12.4 进阶:卡片列表做资源导航
效果:
对应源码:
{% cardurl [url=https://github.com/appotry/hexo] [title=博客源码] [desc=Hexo 主题与内容全部开源] [avatar=/medias_webp/avatar/1.jpg] %}
{% cardurl [url=https://blog.17lai.site/tutorial/] [title=组件教程] [desc=主题全部组件的公开文档] [avatar=/medias_webp/avatar/2.jpg] %}12.5 边界与坑
| 情况 | 行为 |
|---|---|
| 参数不带方括号 | 正则 \[([^=]+)=(.+)\] 不匹配 → 参数被静默忽略,输出空卡片 |
缺 desc | 显示字面量 undefined(源码未判空) |
缺 url | href="undefined",点击跳到 /undefined |
缺 title | 回退显示 url |
| 值里含 HTML/Markdown | 全部 HTML 转义(XSS 防护)→ 显示为实体,不渲染 |
值里含 ] | 正则贪婪(.+)→ 取到最后一个 ] 之前 |
空值 [desc=] | .+ 要求至少 1 字符 → 该参数被忽略 |
键名写错([titel=...]) | 不认识的键被忽略 |
12.6 小结
- 最常用:
{% cardurl [url=...] [title=...] [desc=...] %} - 记方括号:
[key=value]一个都不能少 - 补 desc:不写 desc 会显示
undefined - 加图标:
[avatar=图片URL] - 别塞 HTML:全部转义,只会显示成实体字符
测试断言:页面含 URL 卡片容器标记(
.card-url-wrapper)。
13. 容器 admonition(markdown-it-container)
用途:用
:::语法写提示块 —— 不依赖 Hexo tag,纯 Markdown 层解析。学习路径:最小示例 → 带标题 / 五种类型 → 完整参数 → 进阶 → 边界与坑
13.1 最小示例
效果(页面实际渲染):
这是一个 warning 容器——需要注意的内容!
对应源码:
::: warning
这是一个 warning 容器——需要注意的内容!
:::13.2 常用变体
变体 1:带标题(类型后同一行写标题)
效果:
带标题的容器:类型后同一行写标题。
对应源码:
::: info 可选标题
带标题的容器:类型后同一行写标题。
:::变体 2:五种类型并排
效果:
这是一个 note 容器——补充说明。
这是一个 info 容器——信息提示。
这是一个 attention 容器——注意警示。
这是一个 error 容器——严重错误。
对应源码:
::: note
这是一个 note 容器——补充说明。
:::
::: info
这是一个 info 容器——信息提示。
:::
::: attention
这是一个 attention 容器——注意警示。
:::
::: error
这是一个 error 容器——严重错误。
:::13.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 类型名 | ✅ | — | 必须是根 _config.yml 注册过的 7 个之一 |
| 2 | 可选标题 | ❌ | 无 | 与类型同一行,渲染为容器内标题段 |
| 闭合 | ::: | ✅ | — | 单独一行;缺失会把后续内容全部吞入 |
全部可选值(7 个注册名)
| 名称 | 颜色 |
|---|---|
warning | 橙 |
attention | 橙(同 warning) |
note | 绿 |
info | 蓝 |
tip | 蓝(同 info) |
hint | 蓝(同 info) |
error | 红 |
与 {% note %} tag 的区别
| 维度 | ::: 容器 | {% note %} tag |
|---|---|---|
| 解析层 | markdown-it 插件(Markdown 层) | Hexo tag 插件 |
| 产物 | 裸 <div class="warning">(无 note 类) | <div class="note warning"> |
| 可折叠 | ❌ | ✅(剩余参数作标题) |
| 类型扩展 | 仅注册的 7 个 | 6 色 + no-icon |
13.4 进阶:容器里放列表和代码
效果:
- 先看构建日志有没有报错
- 再看浏览器控制台
- 最后确认是不是缓存
npm run clean && npm run build对应源码:
::: tip 排查顺序
1. 先看构建日志有没有报错
2. 再看浏览器控制台
3. 最后确认是不是缓存
```bash
npm run clean && npm run build
```
:::13.5 边界与坑
| 情况 | 行为 |
|---|---|
未注册类型(::: danger) | 不解析 → ::: 与文字按普通段落原样输出 |
忘记闭合 ::: | 后续整段内容被并入容器 |
| 同名容器嵌套 | markdown-it-container 不支持同名直接嵌套,需用别的内容隔开 |
| 标题写多行 | 只有第一行识别为标题,其余进正文 |
| 样式作用域 | 规则挂在 .post-container 后代选择器下 → wiki 页需 <main> 带该类(已修) |
与 !!! admon 区别 | !!! 产物带 .admonition + 标题段,闭合靠两个空行 |
已废弃:原
{% admonition %}tag 形式已移除(tag 插件不存在);提示块现由 markdown-it-container(:::)与 markdown-it-admon(!!!,见 Markdown 语法篇)提供。
13.6 小结
- 最常用:
::: warning…::: - 想加标题:类型同一行往后写
- 只有 7 个类型,写
danger之类会原样输出 - 和
{% note %}二选一:要折叠就用 tag,要纯 Markdown 写法就用::: - 闭合不能忘,否则后面整段被吞
测试断言:页面含
admonition容器标记。
14. 加密片段 tag({% encrypt %})
用途:文章里某几段内容加密码 —— 每段独立密码,互不影响,解密后原地展开。
学习路径:最小示例 → 标题与简介 → 完整参数 → 进阶(组合 + wiki 整体加密)→ 边界与坑
14.1 最小示例
效果(页面实际渲染,密码 demo123):
对应源码:
{% encrypt demo123 %}
这一段在构建产物里是密文,输入密码后才展开。
{% endencrypt %}14.2 常用变体:带提示标题与简介
效果(密码 test-secret):
对应源码:
{% encrypt "test-secret" "私有内容" "这是一段测试加密内容" %}
**粗体测试** 和 {% note %}内部 note 标签{% endnote %} 结尾。
SECRET-SEGMENT-TEXT-MARKER
{% endencrypt %}14.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 密码 | ✅ | — | 解密密码(Hexo 自动去引号) |
| 2 | 标题 | ❌ | 空 | 加密块提示语(显示在密码框上方) |
| 3 | 简介 | ❌ | 空 | 密码输入框占位文案 |
| 内容 | 正文 | ❌ | 空 | 被加密的 Markdown,可含内部 tag(note/timeline 等) |
14.4 进阶
进阶 1:内部 tag 与 Markdown 混合渲染(解密后二次渲染,after_post_render 方案)
效果(解密后可见):
对应源码:
{% encrypt nested-demo "组合内容" "输入 nested-demo 查看" %}
{% note success 解密后生效 %}
片段里可以正常写 `note`、`label`,以及 **Markdown**。
{% endnote %}
{% label success@已解密 %} {% label info@二次渲染 %}
{% endencrypt %}进阶 2:Wiki 整页加密(在侧栏数据源里配一次,整站 wiki 都加密)
效果(说明):wiki 页首屏显示密码框,解密后侧栏与正文一起展开;同 wiki 其它页面自动解密(派生密钥存 localStorage)。
对应源码:
# source/_data/docs-sidebar.yml
encrypt_password: "你的密码" # 必填,整站 wiki 加密
encrypt_message: 本文已加密 # 可选,提示语
encrypt_placeholder: 请输入密码 # 可选,输入框占位测试断言:L2y(wiki 加密页解密)+ L8 8c(加密 wiki 可访问)。
14.5 边界与坑
| 情况 | 行为 |
|---|---|
| 不写密码 | log.warn + 原样输出明文(等于没加密,最危险) |
| 密码/标题含空格或中文标点 | 必须用引号包裹;否则按空格切成多个参数,密码只取第一段 |
忘记 {% endencrypt %} | 构建报 unclosed tag 错误 |
| 一篇文章多个片段 | 各自独立密码,互不影响 |
| 存储键 | hbe.v4.segment.<页面路径>#<片段序号> → 改文章路径或调整片段顺序,已解密状态失效(需重输) |
| 忘记密码 | 无法恢复(AES-GCM + PBKDF2,无后门) |
| 源文件 | 源 Markdown 仍是明文:仓库公开 = 内容公开,本功能只保护渲染产物 |
| 解密后组件 | 代码块/灯箱/Mermaid/布局统一由 hbe-bundle onSuccess 重新初始化 |
解密后各组件的具体重初始化动作(解密前 DOM 里没有这些元素,必须补一遍):
| 组件 | 重初始化动作 |
|---|---|
| 代码块 | codeWidget() 扫描新 <pre> 生成 toolbar |
| 图片灯箱 | articleInit() 包装 img → lightGallery |
| Mermaid | events.refresh() 重渲染(幂等) |
| 布局 | refreshLayout() 重排 |
14.6 小结
- 最常用:
{% encrypt 密码 %}…{% endencrypt %} - 想加提示:后面跟
"标题" "输入框占位",含空格必须引号 - 能嵌 tag:片段内
note/label解密后正常渲染 - 忘记密码 = 内容丢失,别把唯一密码只存在脑子里
- 整站 wiki 加密:配
docs-sidebar.yml的encrypt_password
加密单测
tools/tests/unit/unit-encrypt.test.js+unit/unit-encrypt2.test.js覆盖:AES-GCM + PBKDF2 闭环 / 片段解密 / 内部 note 渲染 / markdown 粗体 / dk 存储无密码;解密后的运行时行为由门禁 L2y 段(加密解密刷新 + 容器完整性)验证。测试断言:L2y(解密后组件初始化)。
15. 数学公式(MathJax / KaTeX 双通道)
用途:正文写 LaTeX 公式,构建时 SSR 成 SVG —— 不用前端 JS 也能稳定显示。
学习路径:行内 → 块级 → 完整参数(分隔符与引擎)→ 进阶 → 边界与坑
⚠️ 前置条件:文章 front-matter 需要
mathjax: true(specific: true时),主题post.math.engine选数学引擎。
15.1 最小示例:行内公式
效果(页面实际渲染):质能方程
对应源码:
质能方程 $E=mc^2$ 就写在一句话中间。15.2 常用变体:块级公式
效果:
对应源码:
$$
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
$$15.3 完整参数
分隔符支持(按引擎)
| 写法 | mathjax 引擎 | katex 引擎 |
|---|---|---|
$...$ 行内 | ✅ | ✅ |
$$...$$ 块级 | ✅ | ✅ |
\(...\) 行内 | ❌(5.x 硬编码,配置无效) | ✅(需独立成块) |
\[...\] 块级 | ❌ | ✅(需独立成块) |
引擎对照
| 项 | mathjax(默认) | katex |
|---|---|---|
| 生产端 | markdown-it-mathjax3(Node SSR 出 SVG) | markdown-it-texmath |
| 前端 | 加载 MathJax 3.2.2 | 只加载 KaTeX CSS(无需 JS) |
| 产物标记 | <mjx-container> | .katex |
| 切换方式 | post.math.engine: mathjax + 保留 mathjax3 | post.math.engine: katex + 注释 mathjax3、启用 texmath |
配置(根 _config.yml + 主题 _config.yml)
# 根 _config.yml:markdown-it-mathjax3(Node 端 SSR 输出 MathJax SVG)
markdown:
plugins:
- name: markdown-it-mathjax3
options:
tex:
inlineMath: [['$', '$']]
displayMath: [['$$', '$$']]
processEscapes: true
# 主题 _config.yml:post.math 引擎(Fluid 风格,二选一)
post:
math:
enable: false # 开启后文章默认可用
specific: true # true 时需 front-matter `math: true` 才启动
engine: mathjax # Options: mathjax | katex15.4 进阶:矩阵与多行推导
效果:
对应源码:
$$
A = \begin{pmatrix} a & b \\ c & d \end{pmatrix},\quad
\det(A) = ad - bc
$$15.5 边界与坑
| 情况 | 行为 |
|---|---|
写 \(...\) 但用 mathjax 引擎 | 原样输出,不渲染(inlineMath 配置在 5.x 无效) |
| 两种引擎同时启用 | 冲突(都注册 math_inline),只能二选一 |
$ 表示金额(如 $5) | 会被当公式起点 → 用 \$5 转义 |
块级 $$ 与文字同行 | 必须独立成行,否则可能被当行内处理 |
specific: true 但 front-matter 没写 mathjax: true | 主题不注入前端脚本(模板判断的是 page.mathjax,不是 math) |
katex 引擎下 \[...\] | 需独立成块,行内混排不生效 |
| 脑图内的公式 | 走 KaTeX(hexo_markmap.features 含 math) |
15.6 小结
- 最常用:行内
$...$、块级$$...$$(两种引擎都支持) - 记前提:front-matter 写
mathjax: true - 金额要转义:
\$5,否则被当公式 - 别混引擎:mathjax 与 katex 只能二选一
- 公式进脑图:配
hexo_markmap.features: [math]
测试断言:
tools/tests/rich-content-integration.test.js(L2o):公式页mjx-container ≥ 10+katex ≥ 1(双通道)。
16. 脑图 markmap
用途:用 Markdown 列表画出可交互思维导图 —— 缩放、折叠、点链接,适合知识结构梳理。
学习路径:最小示例 → 省略高度 → 完整参数 → 进阶 → 边界与坑
16.1 最小示例
效果(页面实际渲染):
对应源码:
{% markmap 350px %}
- 主题功能测试系列
- 交互视觉篇
- 内容 tag 篇
- 自动化测试
- L1 构建验证
- L13 单测门禁
{% endmarkmap %}16.2 常用变体:不写高度
效果:
对应源码:
{% markmap %}
- 省略高度参数
- 走 CSS 默认高度
{% endmarkmap %}16.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 | 高度 | ❌ | 空(走 CSS 默认) | 原样写入容器 style="height: …",必须带单位(350px) |
| 内容 | 列表 | ✅ | — | 标准 Markdown 无序列表,缩进 2 空格表示层级 |
配置项(根 _config.yml 的 hexo_markmap)
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
CDN | jsdelivr / custom 等 | 插件默认 | 资源来源;本项目用 jsdelivr,再由 filters/markmap-localize.js 本地化 |
features | math / prism / zoom(数组) | [] | 公式、代码高亮、缩放工具栏 |
theme | auto 等 | 插件默认 | 脑图主题,auto 跟随明暗 |
hexo_markmap:
CDN: 'jsdelivr' # 2026-08-24: fastly.jsdelivr.net 不可达,换 cdn.jsdelivr.net
features:
- math # 脑图内公式(KaTeX)
- prism # 代码高亮
- zoom # 缩放
theme: auto16.4 进阶:节点里放链接、行内代码与公式
效果:
对应源码:
{% markmap 320px %}
- 进阶节点
- 链接 [Hexo](https://hexo.io)
- 行内代码 `npm run build`
- 公式 $a^2+b^2=c^2$
{% endmarkmap %}16.5 边界与坑
| 情况 | 行为 |
|---|---|
高度不带单位(350) | style="height: 350" 非法 → 被浏览器忽略,回退 CSS 默认高度 |
| 内容不是列表 | 只有根节点或渲染异常(必须用 - 列表) |
| 缩进层级错乱 | 层级关系按缩进推断,错缩进会挂错父节点 |
features 未含 math | 公式显示为原始 LaTeX 文本 |
未配置 hexo_markmap | 用插件默认 CDN,可能被墙导致空白 |
含 {% encrypt %} 的页面 | 脚本改走串行 onload 加载链(见下),静态页仍用 defer |
| 同页多个 markmap | 各自独立 id,互不影响 |
与加密片段的共存(重要):markmap 的 4 个脚本(d3 / markmap-view / markmap-toolbar / init)由 after_render:html filter 本地化。含 {% encrypt %} 的页面会走串行 onload 链加载器而非 <script defer> —— 原因是 HTML 规范规定动态插入的 <script> 忽略 defer(其 async 默认为 true),hbe 解密流程重新插入脚本时会按下载完成顺序执行,恰好与依赖链颠倒(init 1.8KB 最快先跑 → window.markmap undefined;d3 280KB 最慢最后跑 → markmap-view 报 d3 is not defined)。静态页仍用 defer(并行下载 + 首屏友好)。完整根因与排查教训见 docs/EXPERIENCE.md §58。
16.6 小结
- 最常用:
{% markmap 350px %}+ 无序列表 - 高度必须带单位,否则被忽略
- 层级靠缩进(2 空格一级)
- 要公式/代码高亮:配
hexo_markmap.features: [math, prism] - 和 encrypt 同页:加载链会切换成串行
onload,属预期行为
测试断言:
tools/tests/rich-content-integration.test.js(L2o):.markmap svg渲染。
17. 豆瓣卡片 douban
用途:嵌一本书 / 一部电影 / 一张专辑的豆瓣条目卡(封面 + 评分 + 元数据)。2026-10-19 起改为主题内置本地实现,封面在构建时下载到本地,不再依赖第三方图片代理。
学习路径:最小示例 → 电影与音乐 → 完整参数 → 进阶 → 边界与坑
17.1 最小示例
效果(页面实际渲染):
对应源码:
{% douban book 30376420 %}17.2 常用变体
变体 1:电影
效果:
对应源码:
{% douban movie 24745500 %}变体 2:音乐
效果:
对应源码:
{% douban music 35099703 %}17.3 完整参数
| 位置 | 参数 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| 1 / 2 | type | ✅ | — | book / movie / music 三选一 |
| 1 / 2 | id | ✅ | — | 豆瓣条目 ID,必须匹配 /^\d+$/(纯数字) |
| — | 顺序 | — | — | 用 isNaN 判定:数字那项当 ID、非数字那项当类型,顺序随意 |
配置项(根 _config.yml 的 doubanCard)
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
cookie | 豆瓣 Cookie 字符串 | 空 | 部分条目/接口需要登录态时使用 |
localImages | true / false | true | 封面是否下载到本地;false 时直连豆瓣(会 418) |
imagePath | 目录路径 | source/medias_webp/douban | 本地封面落盘目录 |
imageUrlPrefix | URL 前缀 | /medias_webp/douban | 渲染时拼出的图片访问前缀 |
本地化机制
| 项 | 说明 |
|---|---|
| 代码 | themes/matery/scripts/tags/douban-card.js |
| 依赖 | cheerio + superagent |
| 缓存 | DoubanCard.json(按 url 去重;命中则不访问豆瓣) |
| 封面 | 构建时下载到 source/medias_webp/douban/{type}-{id}.jpg,已存在则跳过 |
| Referer | 按类型内置豆瓣域 Referer(防盗链要求) |
| 暗色 | 遮罩 brightness(0.6) → 暗色 0.4;文字用 var(--white) |
17.4 进阶:type 与 id 顺序互换
isNaN 判定谁是谁,所以 {% douban 30376420 book %} 与 {% douban book 30376420 %} 等价。
效果:
对应源码:
{% douban 30376420 book %}17.5 边界与坑
| 情况 | 行为 |
|---|---|
类型拼错 / ID 非数字(如 30376420a) | Promise.reject → 构建失败(不是静默跳过) |
顺序互换({% douban 30376420 book %}) | 同样有效(isNaN 决定谁是 ID) |
| 豆瓣返回 418(防盗链) | 构建期带类型 Referer 下载到本地,正常显示 |
localImages: false | 直接引用豆瓣原图 → 418,封面空白 |
| 缓存命中 | 不访问豆瓣(断网也能构建) |
| 缓存文件 | 项目根 DoubanCard.json,按 url 去重;删除即强制刷新 |
| 抓取失败 | 降级占位图 + 占位文案(书「见字如晤」/ 影「灯影绰约」/ 乐「乐以忘忧」),不阻塞构建 |
| 封面文件已存在 | 跳过下载(增量构建快) |
背景:豆瓣已收紧防盗链,第三方代理转发会收到 418(图片全站空白)。本地化后一次下载、长期可用。详见
docs/EXPERIENCE.md(豆瓣卡片经验)。
17.6 小结
- 最常用:
{% douban book 30376420 %}(type + 纯数字 ID) - 顺序随意:非数字当类型、数字当 ID
- ID 必须纯数字,否则构建直接失败
- 封面本地化:
localImages: true(默认),别关
附:非 tag 主题功能
以下三项不属于「文章内嵌 tag 插件」范畴(§1–§17),而是主题级独立功能。分组于此以区分 tag 与非 tag 功能边界。
18. 打字机 typing(fun_features)
用途:首页 Banner 副标题逐字打出 —— 首屏的一点点仪式感(与交互视觉篇 §3 的
subtitle.typed同源,配置聚合在fun_features.typing)。学习路径:最小示例 → 变体 → 完整参数 → 边界与坑
18.1 最小示例
效果(说明):首页副标题逐字打出 + 光标闪烁;本文不是首页,实际效果见站点首页(.typed-cursor 元素存在即生效)。
对应源码:
fun_features:
typing:
enable: true # 打字机开关
typeSpeed: 70 # 打印速度,数字越大越慢
cursorChar: "_" # 光标字符
loop: false # 是否循环播放18.2 常用变体:单页关闭
在文章 front-matter 里关掉本页打字机,其余页面不受影响。
效果(说明):该页副标题直接静态显示,无逐字动画。
对应源码:
# 文章 front-matter
subtitle: false18.3 完整参数
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
fun_features.typing.enable | true / false | — | 总开关 |
typeSpeed | 数字 | 主题默认 | 每字间隔,越大越慢 |
cursorChar | 任意字符串 | \| | 光标字符 |
loop | true / false | false | 播完是否重来 |
index.slogan.api | URL | 空 | 从接口取副标题(失败回落 text) |
18.4 边界与坑
| 情况 | 行为 |
|---|---|
| 改配置后页面不变 | Hexo 缓存未失效 → 必须 npm run clean && npm run build |
page.subtitle: false | 仅本页关闭,首页不受影响 |
index.slogan.api 不可达 | 自动回落 text 字段,不会空白 |
与 subtitle.typed 同时配 | 同源配置,以 fun_features.typing 为准 |
18.5 小结
- 最常用:
fun_features.typing.enable: true - 想调速度:
typeSpeed调大更慢 - 单页关掉:front-matter
subtitle: false - 改完必须 clean 构建
测试断言:
tools/tests/rich-content-integration.test.js(L2o):首页.typed-cursor存在。
19. 社交分享 sharejs
用途:文章底部一键分享条(微博 / 微信 / QQ / 豆瓣等),只出现在文章页。
学习路径:最小示例 → 平台子集 → 完整参数 → 备选方案
19.1 最小示例
效果(说明):文章底部出现 .social-share 分享条,按钮顺序由 sites 决定。
对应源码:
sharejs:
enable: true
# 支持顺序:twitter, facebook, google, qq, qzone, wechat, weibo, douban, linkedin
sites: twitter,facebook,qq,qzone,wechat,weibo,douban19.2 常用变体:只留国内平台
效果(说明):分享条只渲染 wechat,weibo,qq 三个按钮。
对应源码:
sharejs:
enable: true
sites: wechat,weibo,qq19.3 完整参数
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
sharejs.enable | true / false | — | 总开关 |
sharejs.sites | 逗号分隔平台名 | 主题默认 | 渲染顺序 = 书写顺序 |
| 可用平台 | twitter / facebook / google / qq / qzone / wechat / weibo / douban / linkedin | — | 不在列表里的不渲染 |
| 渲染范围 | 仅 page.layout == 'post' | — | 首页/独立页不显示 |
19.4 备选方案:addthis(与 sharejs 二选一)
效果(说明):分享条换成 AddThis 的实现,样式与站点风格略有差异。
对应源码:
addthis:
enable: false
pubid: 613a04428f842fe119.5 小结
- 最常用:
sharejs.enable: true+sites列表 - 只在国内用:
wechat,weibo,qq足够 - 顺序即书写顺序
- 别同时开 addthis:二选一
测试断言:
tools/tests/rich-content-integration.test.js(L2o):.social-share组件存在。
20. Live2D 看板娘
用途:页面右下角常驻 Live2D 模型(纯静态,无 PHP 后端),可交互、可换模型。
学习路径:最小示例 → 回访守卫 → 完整参数 → 边界与坑
20.1 最小示例
效果(说明):页面右下角出现看板娘(#live2dcanvas / #waifu);首访不加载,第二次访问才出现。
对应源码:
live2dWidget:
enable: true20.2 常用变体:调回访门槛
效果(说明):默认 viewCount > 1 才加载;把门槛改成 > 0 则首访也加载(牺牲首屏性能)。
对应源码:
# 主题源码中的加载守卫(themes/matery 的 live2d 初始化)
# localStorage.viewCount > 1 才初始化模型20.3 完整参数
| 配置键 | 可选值 | 默认 | 说明 |
|---|---|---|---|
live2dWidget.enable | true / false | — | 看板娘总开关 |
| 模型目录 | 静态文件 | source/live2d/ | Cubism2 / Cubism5 双运行时 |
| 明暗主题 | 自动跟随 | — | 暗色下切换对应模型/配色 |
| 回访守卫 | localStorage.viewCount > 1 | 开启 | 首访不加载,性能保护 |
20.4 边界与坑
| 情况 | 行为 |
|---|---|
| 首次访问看不到 | 预期行为(viewCount > 1 才加载) |
改了 enable 不生效 | 必须 clean 构建(Hexo 缓存) |
| 模型不动 | 检查 source/live2d/ 资源是否完整(skip_render 静态目录) |
| 移动端 | 通常隐藏,避免遮挡内容 |
20.5 小结
- 最常用:
live2dWidget.enable: true - 验证时要刷新两次(回访守卫)
- 模型放
source/live2d/,Cubism2/5 都支持 - 改配置后 clean 构建
测试断言:
tools/tests/rich-content-integration.test.js(L2o):设置viewCount=3后看板娘容器存在。
⚠️ 已知坑
坑 1:标题编号双重叠加
现象:标题渲染为「1. 1. 概述」(两个编号)。
原因:主题 CSS 自动给 h2–h6 加编号(默认开启),手写编号(如 ## 1. 概述)会叠加。
正确做法:手写编号的页面在 front-matter 加 closeAutoTocNum: true;不手写编号的页面不要加该字段。
本文已正确设置
closeAutoTocNum: true。
坑 5:改配置后必须 clean 构建
现象:修改 tag 插件相关配置(如 post.mermaid.enable、live2dWidget.enable、sharejs.enable 等)后页面不变。
原因:Hexo 缓存机制不会自动检测配置变更。
正确做法:改配置后必须 npm run clean && npm run build。
附:tag 插件速查表
| 插件 | 语法 | 关键参数 | 是否需要 frontmatter |
|---|---|---|---|
| note | {% note 颜色 %}内容{% endnote %} | 6 色 | 否 |
| timeline | {% timeline 标题,颜色 %}...{% endtimeline %} | 标题/颜色 | 否 |
| tabs | {% tabs 名称,序号 %}...{% endtabs %} | 名称/序号 | 否 |
| label | {% label 颜色@文字 %} | 颜色在前文字在后 | 否 |
| button | {% button 链接,文字,图标,title %} | 链接第一/文字第二 | 否 |
| githubCard | {% githubCard user:xx repo:yy %} | user/repo | 否 |
| mermaid | {% mermaid %}...{% endmermaid %} | 图表类型 | 是(mermaid: true) |
{% pdf URL %} | URL | 否 | |
| insertmd | {% insertmd 'file.md' %} | 文件名 | 否 |
| wechat_dialog | {% wechat_dialog %}User:/Assistant: 行{% endwechat_dialog %} | User/Assistant 行 | 否 |
| groupimage | {% groupimage 数量 布局 %}{% endgroupimage %} | 数量/布局 | 否 |
| cardurl | {% cardurl [url=xxx] %} | url/title/desc | 否 |
| admonition | ::: warning 内容 ::: | container :::;原 {% admonition %} tag 已废弃 | 否 |
| encrypt | {% encrypt 密码 "标题" "简介" %}...{% endencrypt %} | 密码/标题/简介 | 否 |
| 公式 | $...$ / $$...$$ | mathjax3(SSR SVG)/ katex 引擎 | 是(mathjax: true) |
| markmap | {% markmap 350px %}列表{% endmarkmap %} | 高度/features(math/prism/zoom) | 否 |
| douban | {% douban book\|movie\|music id %} | type/id 顺序可互换;封面本地化 | 否 |
| 打字机 | fun_features.typing | typeSpeed/cursorChar/loop | 否 |
| 分享 | sharejs.sites | twitter/wechat/weibo 等 | 否(仅 post 页) |
| Live2D | live2dWidget.enable | 回访用户守卫 viewCount>1 | 否 |
参考&致谢
系列教程
Hexo 主题功能测试系列
- Hexo主题功能测试-布局与页面篇 —— 页面布局、导航、侧边栏、卡片等结构功能
- Hexo主题功能测试-交互视觉篇 —— 代码高亮、图片灯箱、动画、明暗切换等交互
- Hexo主题功能测试-内容tag篇 —— note / tabs / timeline / label / button 等 tag 插件
- Hexo主题功能测试-Markdown语法篇 —— Markdown 渲染与扩展语法
- Hexo主题功能测试-自动化测试篇 —— 主题功能的自动化测试方案

