本文既是 matery 主题交互视觉功能的完整使用教程,也是 自动化测试靶场。覆盖:代码块增强、图片灯箱、打字机、页面特效、繁简转换、进度条、回顶、打赏、打印。
阅读方式:每个功能都先看 效果(真实渲染或行为描述),再看 对应配置(产出该效果的最小片段),需要查参数时直接跳到「全部可调项」的表。
── 代码与打印 ──
1. 代码块增强
用途:代码块自动带语言标签,并提供复制 / 展开 / 折叠 / 全屏操作,超长代码限高。
学习路径:开语言标签 → 配复制与展开按钮 → 调限高 → 全参数表 → 边界与冲突
1.1 如何启用
效果(本节下面的代码块就是真实渲染结果:右上角语言标签 + 复制按钮,超长块底部出现"展开"条):
# Bash:系统操作
sudo apt update
docker compose up -d
systemctl status nginx# Python:数据处理
def process(data):
"""处理数据并返回结果"""
result = [x * 2 for x in data if x > 0]
return result
print(process([1, -2, 3, 4]))// JavaScript:异步请求
async function fetchData(url) {
const response = await fetch(url);
const data = await response.json();
console.log("数据:", data);
}# YAML:配置文件
server:
host: 0.0.0.0
port: 8080
workers: 4对应配置(最小可用:语言标签 + 复制按钮,其余全关):
code:
language:
enable: true
default: "TEXT"
copy_btn: true标准 Markdown 代码块即可,不需要额外语法 —— 上面四块就是普通的三反引号 + 语言名写法。
1.2 常用选项
① 限高与展开(最常用的一个组合)
效果:下面这段超长 Python 超过 450px 高度阈值,默认折叠,底部出现"展开"条,点开后完整显示(不再限高):
# Python:超长代码示例(超过 450px 高度阈值,触发"展开"按钮)
# 模拟一个简单的博客文章处理流水线
import hashlib
import json
import re
from collections import Counter
from datetime import datetime
from pathlib import Path
def read_posts(directory):
"""读取目录下所有 Markdown 文章"""
posts = []
for path in Path(directory).glob("*.md"):
posts.append(path.read_text(encoding="utf-8"))
return posts
def extract_front_matter(content):
"""提取 Front-Matter 元数据"""
match = re.match(r"^---\n(.*?)\n---\n", content, re.DOTALL)
if not match:
return {}
meta = {}
for line in match.group(1).splitlines():
if ":" in line:
key, value = line.split(":", 1)
meta[key.strip()] = value.strip()
return meta
def analyze_posts(directory):
"""主分析流程:统计词频、生成摘要、计算字数"""
all_words = Counter()
for content in read_posts(directory):
meta = extract_front_matter(content)
body = re.sub(r"^---\n.*?\n---\n", "", content, flags=re.DOTALL)
text = re.sub(r"[#*`>\[\]()]", "", body)
words = re.findall(r"[\u4e00-\u9fa5]|[a-zA-Z]+", text)
all_words.update(words)
word_count = len(words)
summary = " ".join(words[:20])
checksum = hashlib.md5(content.encode()).hexdigest()[:8]
print(f"{meta.get('title', 'untitled')} | {word_count}字 | {checksum}")
print(f" 摘要: {summary}...")
print(f"\n共分析 {len(all_words)} 个词汇")
return all_words
if __name__ == "__main__":
result = analyze_posts("content/posts")
top_words = result.most_common(10)
print("Top 10 高频词:")
for word, count in top_words:
print(f" {word}: {count}")对应配置:
code:
height_limit: "450px" # 超过该高度自动折叠
show_full: true # 折叠态底部给"展开"按钮
shrink: true # 给"主动折叠"按钮② 语言标签兜底文案
效果:未识别的语言(或未写语言)代码块,标签位置显示的是 default 指定的文案;默认值是 TEXT。
对应配置:
code:
language:
enable: true
default: "TEXT" # 换成 "CODE" 则标签显示 CODE③ 复制按钮
效果:右上角出现复制按钮,点击复制整块代码。
对应配置:
code:
copy_btn: true # false = 右上角只剩语言标签④ 全屏按钮
效果:代码块工具栏出现全屏按钮,点击后该代码块铺满视口(这也是 §17 的「代码全屏」)。
对应配置:
code:
show_expand: true全屏按钮是否出现还取决于资源加载 —— 本节未逐一验证各组合,其他值未验证。
⑤ 超宽行换行 / 横向滚动
效果:break: false 时超宽行不换行,改为横向滚动(本文按此值使用)。
对应配置:
code:
break: false⑥ 展开的内部机制(为什么点开就不限高了)
效果:点"展开"后容器 maxHeight 被设为 scrollHeight(完整高度,内部无滚动条,滚动交给页面);点"收起"恢复 maxHeight 为空,重新回到 --code-max-height 的 450px。
对应机制(code-area 组件,非配置项):
- 限高:
code.height_limit(默认"450px")落到 CSS 变量--code-max-height - 展开:折叠态底部显示
scroll-down-bar展开条,点击后 JS 将容器maxHeight设为scrollHeight - 横向滚动:代码容器
overflow-x: auto - 全屏:
code.show_expand: true提供全屏查看
测试断言:页面含
code-area标记(≥4 个代码块);readmode 用例(L11)验证展开后代码完整显示。
1.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
code.language.enable | bool | true | true / false | 右上角语言标签开关 |
code.language.default | string | "TEXT" | 任意字符串 | 语言无法识别时显示的标签文案 |
code.copy_btn | bool | true | true / false | 复制按钮 |
code.show_full | bool | true | true / false | 超长代码的展开/折叠按钮 |
code.shrink | bool | true | true / false | 主动折叠按钮 |
code.height_limit | string | "450px" | 带单位的 CSS 长度(px 等) | 超过该高度自动折叠 |
code.break | bool | — | false(本文件按此值使用) | 超宽行是否换行;false 时改为横向滚动 |
code.show_expand | bool | — | true / false | 代码块全屏查看按钮 |
1.4 边界与冲突
height_limit落到 CSS 变量--code-max-height;展开时 JS 把maxHeight直接设为scrollHeight(不再受该值限制),收起时恢复。copy_btn/show_full/shrink是三个独立按钮,可任意组合;全关则只剩裸代码块。- 语言标签只在
language.enable: true时输出;未识别语言回落language.default。 - 改本段任一项都必须 clean 构建才生效(见「已知坑」坑 5)。
代码块委托幂等(codeWidget)
加密 wiki 页的代码块按钮(折叠/复制)点击无效。根因:codeWidget() 在「页面加载 + 解密后刷新」被调用多次,document 上的 click 委托累积,classList.toggle 执行偶数次后净效果为零。修复:命名 handler(_codeWidgetClickHandler)先 removeEventListener 再 add;Prism 兜底回调加 _codeWidgetPrismRegistered 幂等标记。
测试断言:
readmode-integration.test.js「codeWidget 二次调用后单次点击仍生效」。
Wiki 页行号(prism line-numbers)
Wiki 文档页(layout/wiki.ejs)代码块走 prism 渲染并启用 line-numbers 行号,与文章页代码块增强(toolbar)并存。
测试断言:TC-L15 G5(
tools/tests/wiki-prism-integration.test.js,4 断言):tutorial 页加载 / prism-core / 代码块 / line-numbers 行号。
1.5 小结
- 最小可用:
language.enable+copy_btn两行就够 - 怕长文爆炸:调
height_limit,show_full负责给"展开"条 - 想少一行代码:
break: true换行,不要横向滚动 - 改完不生效:几乎都是缓存 —— clean 构建(坑 5)
── UI 交互与动效 ──
2. 图片灯箱 lightGallery
用途:文章图片点击放大浏览,支持缩放/翻页/下载。
学习路径:开灯箱 → 普通图片即可 → 尺寸注入前置 → 全参数 → 边界与冲突
2.1 如何启用
效果(真实渲染:hover 有阴影 + 边框,点击弹出全屏灯箱,可缩放、翻页,alt 作文案显示):


对应配置:
# 文章图片自动被 articleInit 包装为 .img-item
image_zoom:
enable: true2.2 常用选项
① 普通 Markdown 图片就够
效果:正文 <img> 被 articleInit() 包装成 .img-item,点击弹出 .lg-outer 灯箱(就是上面两张图的行为)。
对应配置(Markdown 源码,无需额外属性):
② 网络图尺寸映射
效果:命中映射表的 CDN 图直接读本地文件取真实尺寸(省一次网络请求),未命中的异步拉取 —— 灯箱打开时图片不会跳一下(防 CLS)。
对应配置:
image_size_plugin:
map_paths:
<CDN 路径>: <本地文件路径>
map_paths的完整结构未在本文件展开,其他值未验证。
③ 灯箱库来源
效果:决定灯箱能力的实现库;换成不可达地址时灯箱整体失效。
对应配置:
libs:
js:
lightgallery: <CDN 地址或本地路径>
libs.js.lightgallery的完整取值形态未在本文件展开,其他值未验证。
2.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
image_zoom.enable | bool | true | true / false | 灯箱总开关 |
libs.js.lightgallery | string | — | 库地址(公共 CDN 或本地路径) | lightGallery 库来源 |
image_size_plugin.map_paths | map | — | CDN 路径 → 本地文件路径 | 网络图映射到本地读尺寸 |
libs.js.lightgallery的完整取值形态未在本文件展开,其他值未验证。
2.4 边界与冲突
- 灯箱依赖
imgsize.js先注入width/height:脚本顺序固定,跳过尺寸注入时打开灯箱会触发重排(CLS)。 - 只作用于正文图片(
articleInit()包装为.img-item),Banner/封面/卡片缩略图不在范围内。 - 尺寸缓存
image_sizes_cache.json30 天 TTL、上限 5000 条;超限后旧条目淘汰,对应图片会重新拉取尺寸。
图片尺寸注入前置(imgsize.js)
灯箱打开前,主题脚本 scripts/events/lib/imgsize.js 会为页面所有 <img> 注入真实 width/height(防 CLS):
- 本地图片:同步读取文件尺寸,直接注入
- 网络图片:命中缓存直接用;命中 CDN→本地映射(
image_size_plugin.map_paths)读本地文件;其余异步拉取 - 缓存持久化:
image_sizes_cache.json(30 天 TTL,上限 5000 条) - 正则兼容:匹配 src 三种形态(带引号/单引号/无引号),兼容 hexo-minify 去引号
该脚本在灯箱绑定之前执行,确保 articleInit() 包装 img → .img-item 时已有尺寸属性,灯箱打开不触发重排。
测试断言:图片被包装为
.img-item,点击弹出.lg-outer。
2.5 小结
- 开箱即用:
image_zoom.enable: true,图片写普通 Markdown 即可 - 只覆盖正文图:封面/缩略图不会进灯箱
- 首屏跳动:查
imgsize.js是否先于灯箱执行,而非改灯箱参数
3. 打字机 Typed
用途:Banner 副标题 / 文章标题逐字打字动画。
学习路径:开副标题打字 → 循环与光标 → 调速 → 全参数 → 边界与冲突
3.1 如何启用
效果:首页 Banner 副标题逐字打出,打完停一下再删除、重新打下一句;页面含 #typed 与 .typed-cursor 标记。
对应配置:
subtitle:
enable: true
typed:
enable: true
subtitle.enable是宿主开关 —— 关掉它,副标题整体不显示,打字机自然无宿主。
3.2 常用选项
① 循环轮播
效果:loop: true 打完一轮继续删掉重打;loop: false 只打一遍(结束后光标是否保留等其他组合未验证)。
对应配置:
subtitle:
typed:
loop: true② 光标外观
效果:showCursor: true 时末尾有闪烁光标(.typed-cursor),字符由 cursorChar 决定 —— 本文件使用下划线 _。
对应配置:
subtitle:
typed:
showCursor: true
cursorChar: "_"③ 速度与启动延迟
效果:typeSpeed 越小打字越快,backSpeed 越小删除越快(50 < 80 即删除比打字快),startDelay 是开打前的停顿。
对应配置:
subtitle:
typed:
startDelay: 100 # 启动延迟(ms)
typeSpeed: 80 # 打字速度(ms/字)
backSpeed: 50 # 删除速度(ms/字)④ 文章页标题打字机
效果:文章页标题也逐字打出(与 Banner 副标题是两套独立开关)。
对应配置:
post:
typed:
enable: true3.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
subtitle.enable | bool | true | true / false | Banner 副标题是否显示(打字机的宿主) |
subtitle.typed.enable | bool | true | true / false | Banner 副标题打字机开关 |
subtitle.typed.loop | bool | true | true / false | 是否循环轮播 |
subtitle.typed.showCursor | bool | true | true / false | 是否显示光标 |
subtitle.typed.cursorChar | string | "_" | 任意单字符 | 光标字符 |
subtitle.typed.startDelay | number | 100 | 毫秒 | 启动前延迟 |
subtitle.typed.typeSpeed | number | 80 | 毫秒/字 | 打字速度(越小越快) |
subtitle.typed.backSpeed | number | 50 | 毫秒/字 | 删除速度(50 < 80 即删除比打字快) |
post.typed.enable | bool | true | true / false | 文章页标题打字机开关 |
3.4 边界与冲突
subtitle.typed.*挂在副标题上:subtitle.enable: false时副标题整体不显示,打字机自然无宿主。loop: false只打一遍(结束后光标是否保留等其他组合未验证)。typeSpeed/backSpeed/startDelay单位都是毫秒,写字符串(如"80ms")不在片段验证范围内。- 改本段后必须 clean 构建(见「已知坑」坑 5)。
3.5 小结
- 只想开打字机:
subtitle.enable+subtitle.typed.enable两行 - 嫌慢:调小
typeSpeed/backSpeed - 文章标题不动:那是
post.typed.enable,与 Banner 无关
测试断言:页面含
#typed或.typed-cursor标记。
4. 页面特效
用途:装饰特效 —— 页面/背景特效(樱花/水波/落叶/雪花/网络/彩带等)与鼠标/点击特效(爱心/星星/烟花等),两组各自互斥单选。
学习路径:开总开关 → 选一个页面特效 → 选一个鼠标特效 → 桌面门控 → 全参数 → 冲突
4.1 如何启用
效果:页面上出现所选装饰动画(如樱花持续飘落),鼠标移动/点击时出现所选点击特效;移动端默认不加载。
对应配置(只开页面特效,鼠标特效保持关闭):
effects:
enable: true
page:
default: sakura # 樱花
mouse:
default: off4.2 常用选项
① 页面/背景特效(单选)
效果:default 指定的那一个才生效 —— 写 sakura 见樱花,写 ripples 见水波,写 off 什么都不加。
对应配置:
effects:
page:
default: off # off | random | 具体 id
available:
sakura: { label: 樱花, lib: sakura }
ripples: { label: 水波, lib: ripples }
leaf: { label: 落叶, lib: leaf }
snowdown: { label: 飘雪, lib: snowdown }
snowflake: { label: 雪花, lib: snowflake }
buble: { label: 冒泡, lib: buble }
canvas_nest: { label: 网络, lib: canvas_nest }
ribbon: { label: 彩带, lib: ribbon }
ribbon_dynamic: { label: 动态彩带, lib: ribbon_dynamic }② 鼠标/点击特效(单选)
效果:点击或移动鼠标时出现对应反馈 —— clicklove 爱心、mouseStar 星星、fireworks 爆炸、popupText 弹出文字。
对应配置:
effects:
mouse:
default: off
available:
clicklove: { label: 爱心, lib: clicklove }
popupText: { label: 弹出文字, lib: popupText }
mouseStar: { label: 星星, lib: star }
fireworks: { label: 爆炸, lib: fireworks }③ 只在桌面端注入
效果:desktop_only: true 时移动端完全不加载特效脚本(省电省流量);设 false 则移动端也加载。
对应配置:
effects:
desktop_only: true④ 运行时切换(不改代码)
效果:打开右下角悬浮面板即可切换页面/鼠标特效,选择写入 localStorage,优先级高于配置里的 default。
对应配置(无需改配置,值存在浏览器):
# localStorage 键(运行时写入,非 _config.yml)
# matery.effect.page → 页面特效 id
# matery.effect.mouse → 鼠标特效 id4.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
effects.enable | bool | true | true / false | 特效总开关 |
effects.desktop_only | bool | true | true / false | 是否只在桌面端注入 |
effects.page.default | string | off | off / random / available 中的任一 id | 页面特效默认项(单选) |
effects.page.available.<id>.label | string | — | 任意显示名 | 面板/菜单显示文案 |
effects.page.available.<id>.lib | string | — | sakura ripples leaf snowdown snowflake buble canvas_nest ribbon ribbon_dynamic | 实际加载的库 |
effects.mouse.default | string | off | off / random / available 中的任一 id | 鼠标特效默认项(单选) |
effects.mouse.available.<id>.label | string | — | 任意显示名 | 面板/菜单显示文案 |
effects.mouse.available.<id>.lib | string | — | clicklove popupText star fireworks | 实际加载的库 |
键名(
<id>)可自定义,但lib必须落在上表列出的库标识内;其他 lib 值未验证。
4.4 边界与冲突
page与mouse两组各自单选:同组内配多个只有default指定的那个生效。desktop_only: true时走desktopGate门控:宽度 >BP.fun(1400px,BP未定义回落 992)且 localStorageviewCount> 1;desktop_only: false跳过门控,移动端也加载。- 运行时面板写入 localStorage
matery.effect.page/matery.effect.mouse,优先级高于配置里的default。 - 改
effects段后必须 clean 构建(见「已知坑」坑 5)。
启用条件(desktopGate,desktop_only 开启时)
- 桌面宽 >
BP.fun(1400px,BP 未定义回落 992) - localStorage
viewCount> 1(防首访即加载) desktop_only: false时跳过门控(移动端也加载)
测试断言:特效脚本按门控加载(
windowWidth > BP.fun)+ 页面/mouse 互斥单选生效。
4.5 小结
- 最小可用:
effects.enable+page.default一行选特效 - 同组配多个没用:只有
default生效 - 桌面看不到:先看宽度是否 > 1400px,再看
viewCount(首访不加载) - 想改主意:用悬浮面板切,别反复改配置
── 国际化与架构 ──
注:本分区按阅读导航分组,§6–§8 功能上分别属于「UI 交互」「UI 交互」「代码与打印」。
5. 繁简转换
用途:页面繁体/简体一键切换。
学习路径:开开关 → 页脚按钮 → 选择如何记忆 → 全参数 → 边界与冲突
5.1 如何启用
效果:页脚出现"繁/简"切换链接(页面含 #translateLink),点击后整页文字在简繁之间转换(JS languageToggle,简繁字库映射)。
用户选择会写入 localStorage(键 targetEncoding_<host>,同时落 cookie),下次访问自动沿用。
对应配置(2026-08-16 迁移聚合至 preference 段,2026-09-08 全站语言配置统一):
preference: # 语言类偏好聚合(原 footer.translate 已删除)
translate:
enable: true # 繁简转换开关5.2 常用选项
① 关闭入口
效果:不再输出 #translateLink,页脚看不到切换链接;但已保存的用户选择不会被清除。
对应配置:
preference:
translate:
enable: false② 用户选择的记忆位置
效果:选择存在 cookie targetEncoding_<host>(不是 localStorage),清 cookie 后回到默认简体。
对应机制(无配置键,仅说明):
cookie: targetEncoding_<host>5.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
preference.translate.enable | bool | true | true / false | 页脚「繁/简」切换按钮开关 |
原
footer.translate已删除,未迁移的其他键名未在本文件出现,其他值未验证。
5.4 边界与冲突
- 旧的
footer.translate不再被读取 —— 残留该段不会报错,但不生效。 - 只有
enable: true才输出#translateLink;关闭后已保存的选择不会被清除。 - 选择存 cookie
targetEncoding_<host>(非 localStorage);清 cookie 后回到默认简体。
5.5 小结
- 只有一行配置:
preference.translate.enable - 配了没反应:检查是否还在写已删除的
footer.translate - 转换后清不掉:清
targetEncoding_<host>cookie
测试断言:页面含
#translateLink或切换按钮。
6. 阅读进度条 / 返回顶部
用途:阅读进度指示 + 一键回到顶部。
学习路径:开进度条 → 调高与颜色 → 开回顶 → 换库 → 全参数 → 边界
6.1 如何启用
效果:页面顶部出现一条细进度条,随滚动从左向右增长;右下角出现返回顶部按钮(点击平滑滚回顶部)。
对应配置:
fun_features:
progressbar:
enable: true
# 返回顶部按钮
backTop:
enable: true6.2 常用选项
① 进度条高度
效果:顶部进度条厚度变化(3 即 3px)。
对应配置:
fun_features:
progressbar:
height_px: 3 # 纯数值,不要写 "3px"② 进度条颜色
效果:进度条颜色变成所填的任意 CSS 颜色(本文件为 #29d 蓝色)。
对应配置:
fun_features:
progressbar:
color: "#29d"③ 进度条库来源
效果:进度条基于 ScrollProgress 库实现,库地址可换公共 CDN 或本地路径;地址不可达时进度条直接缺失。
对应配置:
libs:
js:
scrollProgress: https://lib.baomitu.com/scrollprogress/3.0.2/scrollProgress.min.js④ 动态内容后重算
效果:解密 / 翻页 / 下拉加载等内容变化后调用 refreshLayout(),销毁旧实例并重算 —— 避免监听器叠加导致进度计算错误。
对应机制(注册为布局回调,无配置键):
Matery.events.registerLayoutCallback(function () { /* 重算进度 */ });
Matery.events.refreshLayout();6.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
fun_features.progressbar.enable | bool | true | true / false | 阅读进度条开关 |
fun_features.progressbar.height_px | number | 3 | 像素数值(未带单位) | 进度条高度 |
fun_features.progressbar.color | string | "#29d" | 任意 CSS 颜色 | 进度条颜色 |
backTop.enable | bool | true | true / false | 返回顶部按钮开关 |
libs.js.scrollProgress | string | 公共 CDN 地址(片段所示) | CDN 或本地路径 /libs/scrollprogress/scrollProgress.min.js | 进度条库来源 |
6.4 边界与冲突
height_px是纯数值(键名带_px后缀),不要写成"3px"。libs.js.scrollProgress换成不可达地址时进度条直接缺失(无内置兜底实现)。- 两功能互相独立:关进度条不影响回顶,反之亦然;
backTop是顶层键,不在fun_features下。 - 改本段后必须 clean 构建(见「已知坑」坑 5)。
测试断言:页面含
.progress-bar或#backTop;TC-L15 G6(tools/tests/core-events-integration.test.js,2 断言):布局回调注册 ≥1 且refreshLayout()不抛错。
6.5 小结
- 两个功能、两个键位:
fun_features.progressbar与顶层backTop,互不影响 - 高度写错单位:
height_px只收数字 - 进度条不动:先查
libs.js.scrollProgress是否可达
7. 打赏弹窗
用途:文章打赏按钮 + 微信/支付宝二维码弹窗。
学习路径:开全局入口 → 换标题 → 配两个二维码 → 按篇覆盖 → 全参数 → 边界
7.1 如何启用
效果:文章底部出现"赏"按钮(页面含 #reward),点击弹出二维码 dialog,内含支付宝/微信 tabs,可点关闭按钮或点遮罩关闭。
对应配置:
post:
reward:
enable: true
title: 码字辛苦,打赏作者!
wechat: /medias_webp/reward/wechat.webp
alipay: /medias_webp/reward/alipay.webp7.2 常用选项
① 弹窗标题
效果:弹窗顶部标题变成所填文案(本文件为"码字辛苦,打赏作者!")。
对应配置:
post:
reward:
title: 码字辛苦,打赏作者!② 两个收款码
效果:分别决定"微信"与"支付宝"tab 里显示的二维码图片;只配一个时另一个 tab 无图可展示(tab 是否隐藏未验证)。
对应配置:
post:
reward:
wechat: /medias_webp/reward/wechat.webp
alipay: /medias_webp/reward/alipay.webp③ 单篇覆盖全局
效果:front-matter 写 reward: true 即本篇开启打赏(哪怕全局关着);不写则跟随全局 post.reward.enable。
对应配置:
reward: true # 开启本文打赏(可选,默认按全局)7.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
post.reward.enable | bool | true | true / false | 全局打赏入口开关 |
post.reward.title | string | 码字辛苦,打赏作者!(片段所示) | 任意字符串 | 弹窗标题 |
post.reward.wechat | string | — | 图片路径(建议 webp/png) | 微信收款二维码 |
post.reward.alipay | string | — | 图片路径 | 支付宝收款二维码 |
文章 front-matter reward | bool | — | true / false | 单篇覆盖全局开关 |
7.4 边界与冲突
- front-matter 未写
reward时按全局post.reward.enable;写了以文章值为准。 - 只配一个二维码时另一个 tab 无图可展示(tab 是否隐藏未验证)。
- 图片路径是站点相对路径,走 CDN 替换;换成外链图片时需确认能直连。
7.5 小结
- 全局开:
post.reward.enable: true,再配两张二维码 - 按篇控制:front-matter
reward: true/false - 点开没图:检查路径是否被 CDN 替换成不可达地址
测试断言:页面含
#reward+ 打赏按钮。
8. 打印样式
用途:打印文章时的排版优化。
学习路径:开开关 → 看打印预览 → 全参数 → 边界
8.1 如何启用
效果:只在 @media print 生效 —— 打印预览时隐藏导航/侧栏/页脚,正文居中留白优化,链接显示 URL;屏幕上完全看不出差别。
对应配置:
print:
enable: true8.2 常用选项
① 关闭打印优化
效果:浏览器打印回到页面原样 —— 导航/侧栏照旧打印出来,分页与留白都不优化。
对应配置:
print:
enable: false8.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
print.enable | bool | true | true / false | 打印样式开关 |
8.4 边界与冲突
- 只作用于
@media print:屏幕上完全看不出差别,必须用浏览器打印预览验证。 - 隐藏导航/侧栏/页脚是纯 CSS 规则,不改变 DOM,也不影响屏幕端交互。
- 关掉后浏览器打印仍是页面原样(分页、留白都不会优化)。
8.5 小结
- 只有一行:
print.enable - 验证方式特殊:必须看打印预览,别在屏幕上找差异
- 零风险:纯 CSS,不影响屏幕端
测试断言:CSS 含
@media print规则。
── 媒体与数据可视化 ──
注:本分区按阅读导航分组,§13–§14 属于「UI 交互」,§15/§18 属于「架构」,§16–§17 属于「UI 交互」。
9. 音乐播放器 APlayer
用途:文章/侧边栏嵌入音乐播放器(MetingJS 解析 + APlayer 播放)。
学习路径:开开关 → 选音源与类型 → 填 id → 吸底 → 全参数 → 边界
9.1 如何启用
效果:页面出现音乐卡片(封面 + 播放控制 + 进度条,页面含 aplayer 容器标记),暗色模式自动适配。
对应配置:
music:
enable: true
server: netease # netease/tencent/kugou/xiami/baidu
type: playlist # song/playlist/album/search/artist
id: 190133732 # 网易云歌单/歌曲 ID9.2 常用选项
① 音源与解析类型
效果:server 决定去哪个平台解析,type 决定 id 的含义 —— playlist + 歌单 ID 得到列表,song + 歌曲 ID 得到单曲。两者必须成对。
对应配置:
music:
server: netease
type: playlist
id: 190133732② 吸底模式
效果:fixed: true 时播放器固定在页面底部,与卡片内嵌形态互斥(同一时刻只有一种)。
对应配置:
music:
fixed: false # true = 吸底模式③ 自动播放
效果:autoplay: true 尝试自动开播;但受浏览器自动播放策略限制,无用户交互时通常不会真正发声。
对应配置:
music:
autoplay: false④ 主题色
效果:播放器控件主题色变成所填 CSS 颜色(本文件 #42b983 绿)。
对应配置:
music:
theme: '#42b983'9.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
music.enable | bool | true | true / false | 音乐播放器开关 |
music.server | string | netease | netease / tencent / kugou / xiami / baidu | 音源 |
music.type | string | playlist | song / playlist / album / search / artist | 解析类型(决定 id 的含义) |
music.id | string | number | — | 对应 type 的 ID/关键词 | 歌单/歌曲/专辑/歌手 ID |
music.fixed | bool | false | true / false | 吸底模式 |
music.autoplay | bool | false | true / false | 自动播放 |
music.theme | string | '#42b983' | 任意 CSS 颜色 | 播放器主题色 |
9.4 边界与冲突
type与id必须成对:把playlist改成song而沿用歌单 ID,会解析不到歌曲。fixed: true时播放器固定在页面底部,与卡片内嵌形态互斥(同一时刻只有一种)。autoplay: true会受浏览器自动播放策略限制,无用户交互时通常不会真正发声。- 第三方音源可用性受上游影响,
xiami等服务端已关停,配置仍可写但可能无结果(未在本文件验证)。
9.5 小结
- 最小可用:
enable+server+type+id四项 - 换了类型记得换 id:
type/id是成对的 - 不能自动播放:正常现象,浏览器策略限制
测试断言:页面含
aplayer容器标记。
10. 视频播放器 DPlayer
用途:文章内嵌视频播放器(支持 HLS/直播等)。
学习路径:开开关 → 写 tag → 全参数 → 边界
10.1 如何启用
效果:正文出现视频播放器(播放/暂停/进度/音量,支持弹幕),页面含 dplayer 容器标记。
对应配置:
dplayer:
enable: true10.2 常用选项
① 指定视频源
效果:播放器加载 url 指向的视频;写本地路径则播本地文件。
对应配置(正文里写 tag,不是 YAML):
{% dplayer url=/medias_webp/video/demo.mp4 %}10.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
dplayer.enable | bool | true | true / false | DPlayer 资源加载与 tag 渲染开关 |
tag 参数 url | string | — | 视频地址(本地或远程) | 视频源 |
tag 的其他参数(弹幕等)未在本文件出现,其他值未验证。
10.4 边界与冲突
dplayer.enable: false时 tag 不输出播放器(连容器都没有),不是"显示空白播放器"。- 示例里的 tag 是正文渲染内容,本身不受本篇配置影响 —— 配置是站点级的。
- 本地视频路径写错时播放器仍会渲染,只是视频 404(表现为黑屏)。
10.5 小结
- 站点级开关 + 正文 tag 两件事,别混为一谈
- 黑屏先查路径:播放器渲染成功 ≠ 视频存在
- 关掉是彻底不渲染:不是留一个空播放器
测试断言:页面含
dplayer容器标记。
11. Bilibili 视频卡片
用途:嵌入 Bilibili 视频(iframe 或卡片)。
学习路径:开开关 → 写 BV 号 → 可选覆盖地址 → 全参数 → 边界
11.1 如何启用
效果:正文出现 Bilibili 播放器 iframe,页面含 bilibili 相关容器标记。
对应配置:
bilibili:
enable: true11.2 常用选项
① 按 BV 号嵌入
效果:tag 里的 BV 号决定播放哪个视频(本文件示例即如此)。
对应配置(正文里写 tag):
{% bilibili BV1oa4y1L7mw %}② 统一覆盖播放器地址
效果:配了 iframeUrl 则以它为准,忽略 tag 传来的 BV 号(适合固定嵌入某个直播/播放页)。
对应配置:
bilibili:
# iframeUrl: //player.bilibili.com/player.html?aid=xxx&bvid=xxx
iframeUrl在本文件的片段里是注释状态,启用方式与其他值未验证。
11.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
bilibili.enable | bool | true | true / false | Bilibili 卡片/tag 开关 |
bilibili.iframeUrl | string | —(片段中为注释) | 完整播放器 URL | 可选:覆盖 tag 生成的地址 |
iframeUrl在本文件的片段里是注释状态,启用方式与其他值未验证。
11.4 边界与冲突
- 不配
iframeUrl时按 tag 传入的 BV 号工作(本文件示例即如此);配了则以iframeUrl为准。 enable: false时 tag 不渲染,正文只留空行。- 播放器是 iframe,受上游地区/Referer 策略影响,本地预览可能显示黑屏。
11.5 小结
- 两种用法:按 BV 号(默认)或全局
iframeUrl覆盖 - 本地黑屏:常见现象,属上游策略而非配置错误
- 关掉留空行:正文会出现一段空白
测试断言:页面含 bilibili 相关容器标记。
12. ECharts 图表
用途:文章内嵌 ECharts 交互图表。
学习路径:开开关 → 写 tag(宽/高 + JSON)→ 明暗跟随 → 全参数 → 边界
12.1 如何启用
效果:正文出现可交互图表(柱状/折线/饼图等),容器高度固定占位(防 CLS),切换明暗主题时图表自动重建配色。
对应配置:
echarts:
enable: true12.2 常用选项
① 宽高 + 图表配置
效果:第一个参数是容器宽度('100%' 即占满正文宽),第二个参数是容器高度(400 即 400px),后面是 ECharts option JSON。
对应配置(正文里写 tag):
{% echarts '100%' 400 %}
{
"title": {"text": "示例图表"},
"xAxis": {"type": "category", "data": ["A", "B", "C"]},
"series": [{"data": [10, 20, 30], "type": "bar"}]
}
{% endecharts %}② 明暗主题自动切换
效果:切换明暗主题时,图表 dispose 后按新主题 reinit 再 setOption —— 深色页面上不会留着浅色坐标轴。
对应机制(走统一入口 Matery.echarts.init,无配置键):
Matery.echarts.init(id, option); // 内部监听 theme-changed12.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
echarts.enable | bool | true | true / false | ECharts 库与 tag 开关 |
| tag 第 1 参数 | string | — | CSS 宽度(示例 '100%') | 容器宽度 |
| tag 第 2 参数 | number | — | 像素高度(示例 400) | 容器高度 |
12.4 边界与冲突
- 高度由 tag 第二参数固定(容器先占位,防 CLS),图表不会撑高容器。
- 只有走
Matery.echarts.init(id, option)的图表才随明暗切换 dispose→reinit;自己echarts.init的不会跟随主题。 enable: false时 tag 的输出形态未在本文件验证,但Matery.echarts入口不会注册。
明暗主题自动切换范围(2026-09-06 P11 统一)
主题内部全部 ECharts 图表(文章 tag / 词云 / 雷达图 / 文章统计 3 图 / 日历 / 音乐)统一走 Matery.echarts.init(id, option) —— 监听 theme-changed 事件,切换明暗时自动 dispose + reinit(新主题)+ setOption。容器 CSS 固定高度占位(防 CLS)。
测试断言:页面含 echarts 容器标记 +
Matery.echarts已注册(tools/tests/wordcloud.test.jsL2g 验证明暗切换)。
12.5 小结
- 两件事都要:站点开
echarts.enable,正文写 tag - 高度是固定的:图表超高不会撑开容器,需要自己调第二个参数
- 不跟明暗:多半是没用
Matery.echarts.init而是自己 init
13. AOS 卡片进入动画
用途:首页瀑布流卡片加载时的进入动画。
学习路径:看效果 → 了解触发方式 → 参数位置 → 边界
13.1 如何启用
效果:新卡片(infScroll 下拉加载出来的)进入视口时淡入上移(fade-in-up),已经在视口内的旧卡片不重播。
对应配置:默认启用,无开关;参数在 themes/matery/source/js/boot.js(AOS duration 700ms / delay 100ms,片段值,其他值未验证)。
13.2 实现方式
效果:只有滚动到视口才播动画 —— 靠 IntersectionObserver 监听 .card,进入时才加 .card-aos-enter 类;.article 不加(Masonry 绝对定位,避免重叠)。
对应机制(无 YAML 键):
IntersectionObserver 监听 .card → 进入视口 → 加 .card-aos-enter(fade-in-up, 500ms)
.article 排除在外(Masonry 绝对定位)13.3 全部可调项
| 项 | 位置 | 取值(片段) | 作用 |
|---|---|---|---|
aos.duration | themes/matery/source/js/boot.js | 700ms | 动画时长(其他值未验证) |
aos.delay | themes/matery/source/js/boot.js | 100ms | 动画延迟(其他值未验证) |
13.4 边界与冲突
- 只作用于
.card,.article被排除 —— 否则 Masonry 绝对定位会重叠。 - 卡片动画类自身写的是 500ms(
fade-in-up),AOS 全局参数是 700ms / 100ms,两处数值并存,以实际 CSS/JS 为准。
13.5 小结
- 没有 YAML 开关:默认开,参数在
boot.js - 新卡片才播:靠 IntersectionObserver,不是 CSS 一上来就跑
- 关联 §15:内容动态加载后由
refreshLayout()重新接管
测试断言:下拉加载后新卡片含
.card-aos-enter,且卡片无重叠(tools/tests/infinite-scroll-aos.test.js)。
14. 悬浮面板(right-floating)
用途:右下角齿轮按钮打开的"偏好设置"面板 —— 字体缩放、语言切换(简/繁)、显示模式(亮/自动/暗)、页面特效开关等,选择保存在 localStorage,跨页面持久化。
学习路径:点齿轮 → 字体缩放 → 语言 → 背景 → 持久化 → 边界
14.1 如何启用
效果:面板默认隐藏,点击右下角齿轮按钮(.btn-floating)以 flex 展开,再次点击收起;面板分区:字体 / 语言 / 显示模式 / 特效 / 背景。
对应配置:面板为纯前端组件(layout/_partial/common/right-floating.ejs + floating-panel.styl),无独立配置开关;面板内各选项与主题既有功能联动(语言走 lang.translate、显示模式走 data-user-color-scheme、特效走 effects 开关)。
14.2 常用选项
① 字体缩放
效果:A+ / A− 每步 0.1,范围 0.85–1.3,即时作用于根节点 --font-scale;↺ 一键恢复 1.0;刷新/跨页保持。
对应配置(运行时写 localStorage,非 YAML):
matery.font-scale # 0.85 ~ 1.3,步进 0.1,默认 1.0② 语言区
效果:语言下拉(含国旗,行宽不足时只显国旗)+ 简/繁分段,写入本地偏好并跳转对应语言版。
对应配置:无独立键位 —— 走主题既有语言配置(简体/繁体各自的站点版本)。
③ 背景区
效果:网页背景可选 off / 图片 / 视频;可选项由 background.image.enable / background.video.enable 控制,两者都关只剩 off。
对应配置:
matery.bg # off | image | video(运行时偏好)14.3 全部可调项(面板控件)
| 控件 | 持久化键 | 取值/范围 | 作用 |
|---|---|---|---|
| 字体缩放 | matery.font-scale | 0.85–1.3,步进 0.1 | 根节点 --font-scale |
| 背景模式 | matery.bg | off / image / video | 网页背景(互斥单选) |
| 简/繁切换 | —(未在本文件列出) | 简体 / 繁体 | 跳转对应语言版 |
| 显示模式 | —(未在本文件列出) | 亮 / 自动 / 暗 | data-user-color-scheme |
| 页面特效 | matery.effect.page / matery.effect.mouse | 见 §4 | 切换特效 |
未列出的持久化键名未在本文件出现,其他值未验证。
14.4 边界与冲突
- 面板自身没有总开关;显隐由各分区对应的功能开关间接控制(
effects.enable/background.enable)。 - 字体缩放写的是 CSS 变量
--font-scale,不是font-size—— 直接改font-size会和面板打架。 - localStorage 里残留的偏好优先于配置默认值(如
matery.bg优先bgDefault)。
14.5 小结
- 无配置入口:要改默认行为去改各功能自己的配置,别找面板开关
- 持久化在 localStorage:清理浏览器数据即回到默认
- 字体缩放走 CSS 变量:
--font-scale是单一出口
测试断言:TC-L14(
tools/tests/floating-panel-integration.test.js,8 断言,归 release-test L11):面板默认隐藏 / 齿轮打开 / 字体缩放 / 恢复默认等。
15. 事件总线(Matery.events)
用途:主题内部组件通信的统一事件总线,解决"内容动态加载后组件需重新初始化"的问题 —— 解密、翻页、下拉加载等场景触发对应刷新,各组件注册回调即可,无需互相耦合。
学习路径:注册回调 → 触发刷新 → 区分全量/布局 → API 全表 → 边界
15.1 如何启用
效果:注册的回调在对应刷新时被调用 —— 全量刷新会让 MathJax/mermaid/Prism 重新渲染,布局刷新只重排(AOS/Masonry/进度条),两者互不触发。
对应配置(themes/matery/source/js/events.js):
// 内容动态加载后需要重渲染 → 全量刷新
Matery.events.registerRefreshCallback(function () { /* 重渲染 */ });
// 仅布局变化(resize/内容高度变化)→ 轻量布局刷新
Matery.events.registerLayoutCallback(function () { /* 重排 */ });
Matery.events.refreshLayout();15.2 常用选项
① 全量刷新(重渲染内容)
效果:触发后所有 registerRefreshCallback 注册的回调依次执行 —— 用于解密后、翻页后重新渲染公式/图表/代码高亮。
对应配置:
Matery.events.registerRefreshCallback(cb);
Matery.events.refresh();② 布局刷新(只重排,不重渲染)
效果:只跑 registerLayoutCallback 的回调,不重复触发布局无关的重渲染 —— 比 refresh 轻,适合窗口 resize、内容高度变化。
对应配置:
Matery.events.registerLayoutCallback(cb);
Matery.events.refreshLayout();15.3 全部可调项(API)
| 方法 | 作用 |
|---|---|
Matery.events.registerRefreshCallback(cb) | 注册全量刷新回调(内容重渲染:MathJax/mermaid/Prism 等) |
Matery.events.registerLayoutCallback(cb) | 注册布局刷新回调(仅重算布局:AOS/Masonry/进度条) |
Matery.events.refresh() | 触发全部 refresh 回调 |
Matery.events.refreshLayout() | 触发全部 layout 回调(轻量,不重渲染内容) |
15.4 边界与冲突
- refresh 与 refreshLayout 隔离:全量刷新不重复触发布局,布局刷新不重渲染内容。
- 解密后统一走 refresh(见内容 tag 篇 §14 / 布局篇 §11)。
- 回调里再做重活要注意幂等 —— 重复触发时不能重复绑定监听器(参考 §1.4 codeWidget 的教训)。
15.5 小结
- 先想清楚要哪种:内容变了 →
refresh;只是尺寸变了 →refreshLayout - 轻量优先:能用
refreshLayout就别用refresh - 注册即生效:不需要额外的初始化步骤
测试断言:TC-L15 G3(
tools/tests/core-events-integration.test.js,4 断言):API 存在 / refresh 触发全量 / refreshLayout 触发布局 / 隔离性。
16. Banner 明暗切换
用途:Banner 背景随明暗主题切换联动 ——
theme-changed事件派发后,body 背景在明暗两套样式间切换;背景模式(图片/视频/关闭)由面板偏好matery.bg控制。学习路径:切明暗看联动 → 选背景模式 → 设默认值 → 全参数 → 边界
16.1 如何启用
效果:点击显示模式切换(亮/暗)后,Banner 与 body 背景同步换色 —— 不会出现"文字变白了但背景还是白的"。
对应配置:
bgDefault: image # 无本地偏好时的默认背景模式(off / image / video)16.2 常用选项
① 运行时背景模式
效果:在悬浮面板选背景模式,选择存 localStorage matery.bg,优先级高于 bgDefault;清 localStorage 后回落配置值。
对应配置:
matery.bg # off | image | video(互斥单选,图片/视频二选一)② 面板里哪些背景可选
效果:background.image.enable / background.video.enable 关掉后,面板对应选项不可选;两者都关只剩 off。
对应配置:
background:
enable: true
image:
enable: true
video:
enable: true16.3 全部可调项
| 键 | 类型 | 默认 | 可选值/范围 | 作用 |
|---|---|---|---|---|
bgDefault | string | — | off / image / video | 无本地偏好时的默认背景模式 |
matery.bg(localStorage) | string | — | off / image / video | 运行时背景偏好(互斥单选) |
background.enable | bool | — | true / false | 面板「背景区」总开关 |
background.image.enable | bool | — | true / false | 面板「图片」选项是否可选 |
background.video.enable | bool | — | true / false | 面板「视频」选项是否可选 |
背景图片/视频的具体地址键未在本文件展开,其他值未验证。
16.4 边界与冲突
matery.bg(本地偏好)优先于bgDefault;清除 localStorage 后回落到配置值。background.image.enable/background.video.enable关掉后,面板对应选项不可选;两者都关只剩off。- 联动靠
theme-changed事件:手动改data-user-color-scheme而不派发事件,Banner 背景不会跟着变。
16.5 小结
- 默认值在
bgDefault,用户选择在 localStorage,后者优先 - 联动靠事件不是靠 CSS:绕过
theme-changed就不会同步 - 想彻底关背景区:
background.enable: false
测试断言:
tools/tests/banner-bg.test.jsT5:body 背景明暗变化(theme-changed 派发)。
17. 阅读模式与代码全屏(2026-09-06 分离为两个独立功能)
用途:
- 阅读模式(read_mode):文章卡片一键全屏沉浸阅读,隐藏页面其他元素
- 代码全屏(code-fullscreen):单个代码块容器全屏查看,与阅读模式相互独立(类加在代码容器而非卡片)
学习路径:进阅读模式 → 看悬浮球 → 退出动画 → 代码全屏 → 展开机制 → 边界
17.1 如何启用
效果:文章信息栏(post-info)点阅读模式按钮(#read_mode),整个文章卡片铺满屏幕(.card-block-fullscreen),页面其他元素被隐藏;再点一次退出。代码块工具栏的全屏按钮(.code-area .code-fullscreen)则只让该代码容器全屏(.code-area.code-block-fullscreen)。
对应配置:两个按钮都是内置 UI,无 YAML 开关;行为由 layout 与 styl 决定。
17.2 常用选项
① 进入动画与悬浮球
效果:卡片以 elastic 1s 弹入;悬浮球延迟淡入(readmode-ball-fade .3s ease .75s both)—— elastic transform 会建立 containing block 让 fixed 悬浮球跳动,延迟淡入正好避开。
对应机制:
.card-block-fullscreen { animation: elastic 1s; }
readmode-ball-fade { .3s ease .75s both }
悬浮球 { position: fixed; right: 1em; top: 70px; z-index: 10000 }
top: 70px避开导航栏 bottom 64;z-index: 10000高于导航栏 997。
② 退出动画
效果:退出时 card-block-fullscreen-exit 淡出(scale .98 + opacity 0),220ms 后才移除全屏类 —— 否则悬浮球会瞬间跳回原位。
对应机制:
exit 类 → 淡出(scale .98 + opacity 0)→ 220ms → 移除全屏类③ 代码块展开(与 §1 限高联动)
效果:点展开条后 pre.style.maxHeight = pre.scrollHeight + 'px'(完整高度,无内部滚动条,页面滚动);收起时 maxHeight='',恢复 --code-max-height 450px。
对应机制:
pre.style.maxHeight = pre.scrollHeight + 'px'; // 展开
pre.style.maxHeight = ''; // 收起 → 回到 450px17.3 全部可调项
| 项 | 位置 | 值(片段) | 作用 |
|---|---|---|---|
| 卡片全屏类 | layout / styl | .card-block-fullscreen | 阅读模式全屏 |
| 悬浮球定位 | styl | right: 1em; top: 70px; z-index: 10000 | 避开导航栏、盖在其上 |
| 退出类 | styl | .card-block-fullscreen-exit | scale .98 + opacity 0 |
| 代码全屏类 | styl | .code-area.code-block-fullscreen | 只全屏单个代码块 |
以上为样式类名与数值,改它们属于改源码,不是改配置。
17.4 边界与冲突
- 阅读模式与代码全屏是两个独立类:进阅读模式不会让代码块全屏,反之亦然。
- 悬浮球必须延迟淡入 —— 去掉延迟会看到悬浮球跳动(elastic 建立 containing block)。
- 退出动画必须等 220ms 再移除类,否则悬浮球跳回。
z-index: 10000是硬编码值,导航栏是 997;改导航栏层级时要同步检查这里。
17.5 小结
- 两个功能别混:卡片全屏 vs 代码容器全屏
- 动画时序是刚需:延迟淡入 + 退出等 220ms,删掉就跳
- 与代码块增强联动:展开改用
scrollHeight,不受 450px 限制
测试断言:
tools/tests/readmode-integration.test.js(L2f PASS=14):阅读模式进入/退出、悬浮球、代码全屏、展开后代码完整显示。
18. JS 加载分层与去 jQuery(2026-09-05 P4/P5)
用途:移除全站 jQuery 依赖(原生 DOM API 重写),并按首屏优先级分层加载脚本,降低首屏阻塞。
学习路径:看副作用 → 确认 jQuery 已停用 → 看分层策略 → 边界
18.1 如何启用
效果:页面不再请求全局 jQuery 库;核心脚本带 defer 不阻塞首屏解析;统计脚本 async;首屏关键内联脚本同步注入,先于所有 defer 执行。
对应配置:
libs:
js:
# jquery: ... # 已注释停用(P4/P5 全站去 jQuery 完成)
# jqueryUI: ... # 已注释停用
# jqueryBarrager: ... # 已注释停用
jqueryPjax: ... # 保留(独立库,非全局 jQuery)18.2 常用选项
① 分层策略
效果:核心脚本延后但不阻塞解析,统计类完全不参与首屏,内联关键脚本抢在最前 —— 保证依赖顺序又不等网络。
对应配置:
核心脚本(materialize/masonry/imagesloaded/aos/events/plugins)→ defer
统计类(umami/busuanzi) → async
首屏关键脚本 → 内联同步(先于 defer)② 特效库原生重写
效果:ripples 特效用原生重写,去掉了对 jQuery 的依赖(contact 页已删除)。
对应机制:见 §4 的 effects.mouse / effects.page 配置,行为不变、实现不依赖 jQuery。
18.3 全部可调项
| 项 | 位置 | 取值(片段) | 作用 |
|---|---|---|---|
libs.js.jquery | 主题 _config.yml | 已注释 | 全局 jQuery(停用) |
libs.js.jqueryUI | 主题 _config.yml | 已注释 | jQuery UI(停用) |
libs.js.jqueryBarrager | 主题 _config.yml | 已注释 | 弹幕库(停用) |
libs.js.jqueryPjax | 主题 _config.yml | 保留 | 独立库,非全局 jQuery |
核心脚本 defer | layout | 统一 defer | 不阻塞首屏解析 |
统计脚本 async | layout | 统一 async | 不参与首屏 |
| 首屏关键脚本 | layout | 内联同步 | 先于 defer 执行 |
18.4 边界与冲突
- 停用 jQuery 后,任何依赖
$/jQuery的内联脚本或第三方插件都会报错 —— 加新库前先确认它是否自带 jQuery 依赖。 defer保证按文档顺序执行,但不保证在 DOMContentLoaded 之前 —— 依赖顺序敏感的逻辑要么内联,要么显式监听事件。- 分层是全局策略,不是逐页开关。
18.5 小结
- jQuery 已全站移除:
libs.js.jquery*三段是注释状态,不是可用开关 - 新增脚本先想优先级:核心
defer/ 统计async/ 首屏内联 jqueryPjax是例外:名字里有 jQuery,但它是独立库
测试断言:页面无全局 jQuery 请求;核心脚本带
defer。
⚠️ 已知坑
坑 1:标题编号双重叠加
现象:标题渲染为「1. 1. 概述」(两个编号)。
原因:主题 CSS 自动给 h2–h6 加编号(默认开启),手写编号(如 ## 1. 概述)会叠加。
正确做法:手写编号的页面在 front-matter 加 closeAutoTocNum: true;不手写编号的页面不要加该字段。
本文已正确设置
closeAutoTocNum: true。
坑 5:改配置后必须 clean 构建
现象:改了 effects/subtitle.typed/image_zoom 等配置但页面不变。
原因:Hexo 缓存机制不会自动检测配置变更。
正确做法:改配置后必须 npm run clean && npm run build。仅改文章内容(如图片/文字)不需要 clean,增量构建正常工作。
附:交互视觉功能速查表
| 功能 | 配置键 | 参数要点 | 自动化断言标记 |
|---|---|---|---|
| 代码块 | code | language/copy_btn/show_full | code-area |
| 灯箱 | image_zoom + libs.js.lightgallery | enable | .img-item / .lg-outer |
| 打字机 | subtitle.typed / post.typed | loop/showCursor/速度 | #typed |
| 特效 | sakura 等 10+ 开关 | 桌面+访问>2 门控 | windowWidth > BP.fun |
| 繁简 | lang.translate.enable | 聚合配置(原 zh_default/footer.translate 删除) | #translateLink |
| 进度条 | progressbar | height/color | .progress-bar |
| 回顶 | backTop | enable | #backTop |
| 打赏 | post.reward | wechat/alipay 图 | #reward |
| 打印 | print | enable | @media print |
| APlayer | music | server/type/id | aplayer |
| DPlayer | dplayer | url | dplayer |
| Bilibili | bilibili | iframeUrl | bilibili |
| ECharts | echarts | JSON 配置 | echarts |
| AOS 动画 | aos(boot.js) | duration/delay | .card-aos-enter |
| 悬浮面板 | right-floating(ejs) | 字体缩放/语言/主题/特效 | #floating-panel |
| 事件总线 | Matery.events | refresh/refreshLayout | Matery.events |
| Banner 明暗 | matery.bg(localStorage) | off/image/video | body 背景 |
| Wiki 行号 | prism line-numbers | wiki 页代码块 | .line-numbers |
| 阅读模式 | #read_mode(post-info 按钮) | 卡片全屏 + 悬浮球 | .card-block-fullscreen |
| 代码全屏 | .code-area .code-fullscreen | 容器全屏(独立于阅读模式) | .code-block-fullscreen |
| JS 加载分层 | libs.js(jquery 注释停用) | 核心 defer / 统计 async / 首屏内联同步 | 无全局 jQuery 请求 |
参考&致谢
系列教程
Hexo 主题功能测试系列
- Hexo主题功能测试-布局与页面篇 —— 页面布局、导航、侧边栏、卡片等结构功能
- Hexo主题功能测试-交互视觉篇 —— 代码高亮、图片灯箱、动画、明暗切换等交互
- Hexo主题功能测试-内容tag篇 —— note / tabs / timeline / label / button 等 tag 插件
- Hexo主题功能测试-Markdown语法篇 —— Markdown 渲染与扩展语法
- Hexo主题功能测试-自动化测试篇 —— 主题功能的自动化测试方案

