在Hexo-fluid的分类页中增加说明(2)--注入法

在Hexo-fluid的分类页中增加说明(2)--注入法

之前发过一篇文章:在Hexo-fluid的分类页中增加说明,实现在分类页面增加说明介绍分类,是通过修改主题模板文件实习的,如果后期主题更新还需要手动再次修改,不太方便,这次实现不修改主题源码,数据驱动的内容注入

背景

在使用 Hexo + Fluid 主题时,我需要在分类页(/categories/)顶部添加一个“专栏说明”板块,用于展示不同分类的介绍、图标和自定义颜色。要求:

· 不修改主题源文件(便于后续升级)
· 内容通过配置文件管理(无需改动代码)
· 位置精确(位于分类列表之前)

经过探索,最终采用 Hexo 官方的 after_render:html 过滤器实现。下面分享完整的实现过程。

最终效果参见本站的分类页

一、整体思路

  1. 监听生成事件:使用 after_render:html 过滤器,在每个 HTML 文件生成后执行。
  2. 识别目标页面:判断当前文件路径是否为 categories/index.html。
  3. 注入样式:在前插入专栏卡片所需的 CSS。
  4. 读取数据:从 source/_data/ 目录下的 YAML 文件中读取专栏配置。
  5. 生成 HTML:遍历数据,生成与主题结构兼容的卡片 HTML。
  6. 插入到正确位置:通过正则匹配分类列表容器(<div class="category-list">),将卡片 HTML 插入其前面。
  7. 返回修改后的内容。

二、具体实现

1. 引入 Font Awesome(可选)

本方案使用 Font Awesome 免费图标库。请确保你的主题已引入 Font Awesome CSS,例如在主题配置或全局布局中添加:

1
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">

如果未引入,也可以在注入脚本中一并注入(见下文脚本注释)。

  1. 创建数据文件

在 source/_data/ 目录下新建:
column_descriptions.yml

1
2
3
4
5
6
7
8
9
10
11
columns:
- name: "技术随笔"
subtitle: "编程与开发心得"
icon: "fas fa-code"
description: "分享前端、后端、DevOps 等技术实践。"
color: "#1890ff"
- name: "生活杂谈"
subtitle: "日常思考与感悟"
icon: "fas fa-pen-fancy"
description: "记录生活中的点滴与读书笔记。"
color: "#f5222d"

· icon 字段直接填写 Font Awesome 的完整类名(如 fas fa-code)。
· color 用于卡片边框、图标和高亮颜色。

注:name、subtitle、icon、description、color 请根据需求自行调整

  1. 编写注入脚本

在博客根目录创建 scripts/ 文件夹(如果没有),然后新建:
categories-inject.js

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
88
89
90
91
92
93
94
95
96
97
98
// scripts/categories-inject.js
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');

hexo.extend.filter.register('after_render:html', function(str, data) {
// 仅处理分类页
if (data.path !== 'categories/index.html') return str;

// 1. 注入自定义 CSS
if (!str.includes('/css/column-cards.css')) {
str = str.replace('</head>', '<link rel="stylesheet" href="/css/column-cards.css"></head>');
}

// 可选:如果主题没有引入 Font Awesome,可以在这里注入
if (!str.includes('font-awesome')) {
str = str.replace('</head>', '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"></head>');
}

// 2. 读取专栏数据
const dataPath = path.join(hexo.source_dir, '_data', 'column_descriptions.yml');
let columns = [];
if (fs.existsSync(dataPath)) {
try {
const content = fs.readFileSync(dataPath, 'utf8');
const yamlData = yaml.load(content);
columns = yamlData && yamlData.columns ? yamlData.columns : [];
} catch (e) {
console.error('读取专栏数据失败:', e);
}
}

if (!columns.length) return str;

// 3. 生成专栏卡片 HTML
const cardsHtml = generateCardsHtml(columns);

// 4. 插入到分类列表之前(正则匹配 class="category-list" 的 div)
const categoryListRegex = /<div\s+class="category-list"[^>]*>/i;
const match = str.match(categoryListRegex);
if (match) {
const insertPosition = match.index;
str = str.slice(0, insertPosition) + cardsHtml + str.slice(insertPosition);
} else {
// 回退:插入到 <body> 后
str = str.replace('<body>', '<body>\n' + cardsHtml);
}

return str;
});

// 生成卡片 HTML(适配 Font Awesome)
function generateCardsHtml(columns) {
let html = '<div class="columns-container">';
for (const col of columns) {
const color = col.color || '#1890ff';
const rgb = hexToRgb(color);
html += `
<div class="column-card" style="--column-color: ${color}; --column-color-rgb: ${rgb.join(',')};">
<div class="card-header">
<i class="${col.icon || 'fas fa-circle-info'}"></i>
<div>
<h3>${escapeHtml(col.name)}专栏</h3>
${col.subtitle ? `<p class="subtitle">${escapeHtml(col.subtitle)}</p>` : ''}
</div>
</div>
<div class="card-content">
<p>${escapeHtml(col.description)}</p>
</div>
</div>
`;
}
html += '</div>';
return html;
}

// 辅助函数:十六进制转RGB
function hexToRgb(hex) {
hex = hex.replace('#', '');
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
return [r, g, b];
}

// 辅助函数:HTML转义
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}
  1. 编写卡片样式

在 source/css/ 目录下创建:

column-cards.css

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
/* 专栏卡片容器 */
.columns-container {
display: grid;
gap: 1.8rem;
margin: 2rem 0;
}

/* 单个卡片 */
.column-card {
--column-color: #1890ff;
--column-color-rgb: 24, 144, 255;
--hover-color: #40a9ff;

padding: 1.5rem;
border-radius: 12px;
border-left: 4px solid var(--column-color);
background: linear-gradient(135deg, rgba(var(--column-color-rgb), 0.05), rgba(var(--column-color-rgb), 0.01));
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}

.column-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
border-left-color: var(--hover-color);
}

.card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.8rem;
}

.card-header i {
font-size: 2rem;
color: var(--column-color);
}

.card-header h3 {
margin: 0;
font-size: 1.4rem;
font-weight: 600;
color: var(--column-color);
}

.subtitle {
margin: 0;
font-size: 0.9rem;
color: #666;
font-style: italic;
}

.card-content p {
margin: 0;
line-height: 1.6;
color: #333;
}

/* 响应式 */
@media (max-width: 768px) {
.columns-container {
gap: 1rem;
}
.card-header {
flex-wrap: wrap;
}
}
  1. 安装依赖

脚本中使用了 js-yaml 解析 YAML 文件,需要安装:

1
npm install js-yaml --save

如果你不想增加依赖,可以将数据文件改为 JSON 格式(column_descriptions.json),然后用 JSON.parse 读取。

  1. 测试
1
hexo clean && hexo generate

打开 public/categories/index.html,检查:

1
2
· <head> 中是否包含 <link rel="stylesheet" href="/css/column-cards.css">
· <div class="category-list"> 之前是否存在 .columns-container 结构

启动本地服务器 hexo server,访问分类页查看效果。

三、总结

通过 after_render:html 过滤器,实现了对 Hexo 主题的完全无侵入式修改:

这种思路不仅适用于 Fluid 主题,也可以推广到任何 Hexo 主题。

附:完整文件结构

1
2
3
4
5
6
7
8
9
your-blog/
├── scripts/
│ └── categories-inject.js # 注入脚本
├── source/
│ ├── _data/
│ │ └── column_descriptions.yml # 专栏数据
│ └── css/
│ └── column-cards.css # 卡片样式
└── (其他Hexo文件)

希望本文可以对你有所帮助

 

鸣心/Write

在Hexo-fluid的分类页中增加说明(2)--注入法
https://b.wihi.top/posts/2da73f2a.html
作者
鸣心
发布于
2026年4月4日
许可协议