给安知鱼主题侧边栏调用随机推荐的文章

我们在上一篇文章之中,实现了在首页调用侧边栏的 最近发布 栏目,详细修改教程,大家可以参阅 安知鱼主题侧边栏最近发布的修改 这篇文章,我们今天来给侧边栏添加一个 随机发布 的栏目,在很多传统的博客中,这个栏目不多见,只是一些门户网站中常见。

代码借助了安知鱼主题默认的 最近发布CSS 样式,所以有些内容与默认的 最近发布 板块类似,下面我们来看实现的过程。

1.添加主题设置

打开主题文件夹下面的 _config.yml 文件,搜索 # aside (侧边栏) ,在 aside: 里面添加如下设置

1
2
3
4
5
6
# 新增:随机文章组件配置
card_random_post:
enable: true # 是否开启随机文章组件
limit: 5 # 显示的随机文章数量
date_enable: true # 是否显示文章发布日期
date_format: "YYYY-MM-DD" # 日期格式

2.添加标题设置

打开根目录下面的 /themes/anzhiyu/languages 文件夹,在里面找到 default.yml 文件,打开之后找到如下代码

1
2
3
4
5
6
7
8
9
aside:
articles: 文章
tags: 标签
categories: 分类
card_announcement: 公告
card_categories: 分类
card_tags: 标签
card_archives: 归档
card_recent_post: 最近发布

代码之后还有很多行代码,主要是在

1
card_recent_post: 最近发布

下面,添加如下代码

1
card_random_post: 随机推荐

添加之后的效果

1
2
3
4
5
6
7
8
9
10
11
aside:
articles: 文章
tags: 标签
categories: 分类
card_announcement: 公告
card_categories: 分类
card_tags: 标签
card_archives: 归档
card_recent_post: 最近发布
card_random_post: 随机推荐
card_webinfo:

3.添加随机调用代码

打开 /themes/anzhiyu/layout/includes/widget 文件夹,在里面新建 card_random_post.pug 文件,之后复制下面的代码粘贴到文件中

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
99
100
101
102
103
104
105
106
107
108
109
110
111
//- 侧边栏随机文章卡片(稳定版:逻辑清晰+刷新即变+样式统一)
if theme.aside.card_random_post.enable && site.posts.length > 0
.card-widget.card-recent-post
.item-headline

i.anzhiyufont.anzhiyu-icon-dice-d20
span= _p('aside.card_random_post')

//- 1. 隐藏的完整文章数据源(彻底隐藏,不显示)
#random-posts-data.random-posts-data
- site.posts.each(function(article) {
// 逐行声明变量,逻辑清晰,避免Pug解析报错
- var articleLink = url_for(article.link || article.path);
- var articleTitle = article.title || _p('no_title');
- var articleCover = article.cover ? url_for(article.cover) : '';
- var noCoverClass = article.cover === false || !theme.cover.aside_enable ? 'no-cover' : '';
- var articleDate = date(article.date, config.date_format);
- var articleUpdated = date(article.updated, config.date_format);
- var articleDateXml = date_xml(article.date);
- var articleUpdatedXml = date_xml(article.updated);

// 用多个data属性存储数据,绕开Pug解析JSON的坑
div(
data-link=articleLink,
data-title=articleTitle,
data-cover=articleCover,
data-no-cover=noCoverClass,
data-date=articleDate,
data-updated=articleUpdated,
data-date-xml=articleDateXml,
data-updated-xml=articleUpdatedXml
)
- })

//- 2. 随机文章显示容器
#random-posts-display.aside-list

//- 3. 前端随机逻辑(ES5写法,稳定无兼容问题)
script.
// 页面加载完成后执行
window.onload = function() {
// 读取主题配置参数
var showLimit = !{theme.aside.card_random_post.limit === 0 ? site.posts.length : theme.aside.card_random_post.limit || 5};
var sortType = !{JSON.stringify(theme.aside.card_random_post.sort || 'date')};
var errorImage = !{JSON.stringify(url_for(theme.error_img.post_page))};
var textUpdated = !{JSON.stringify(_p('post.updated') || '更新于')};
var textCreated = !{JSON.stringify(_p('post.created') || '创建于')};

// 步骤1:获取所有文章数据
var allPostNodes = document.querySelectorAll('#random-posts-data > div');
var allPosts = [];
for (var i = 0; i < allPostNodes.length; i++) {
var node = allPostNodes[i];
allPosts.push({
link: node.dataset.link,
title: node.dataset.title,
cover: node.dataset.cover,
noCover: node.dataset.noCover,
date: node.dataset.date,
updated: node.dataset.updated,
dateXml: node.dataset.dateXml,
updatedXml: node.dataset.updatedXml
});
}

// 步骤2:Fisher-Yates洗牌算法(经典随机,结果均匀)
for (var j = allPosts.length - 1; j > 0; j--) {
var randomIndex = Math.floor(Math.random() * (j + 1));
var temp = allPosts[j];
allPosts[j] = allPosts[randomIndex];
allPosts[randomIndex] = temp;
}

// 步骤3:截取需要显示的数量
var randomPosts = allPosts.slice(0, showLimit);

// 步骤4:渲染到页面
var displayContainer = document.getElementById('random-posts-display');
var htmlContent = '';
for (var k = 0; k < randomPosts.length; k++) {
var post = randomPosts[k];

// 拼接封面图HTML
var coverHtml = '';
if (post.cover && post.noCover === '') {
coverHtml = '<a class="thumbnail" href="' + post.link + '" title="' + post.title + '">' +
'<img src="' + post.cover + '" onerror="this.onerror=null;this.src=\'' + errorImage + '\'" alt="' + post.title + '">' +
'</a>';
}

// 拼接日期HTML
var dateHtml = '';
if (sortType === 'updated') {
dateHtml = '<time datetime="' + post.updatedXml + '" title="' + textUpdated + ' ' + post.updated + '">' + post.updated + '</time>';
} else {
dateHtml = '<time datetime="' + post.dateXml + '" title="' + textCreated + ' ' + post.date + '">' + post.date + '</time>';
}

// 拼接单篇文章的完整HTML
htmlContent += '<div class="aside-list-item ' + post.noCover + '">' +
coverHtml +
'<div class="content">' +
'<a class="title" href="' + post.link + '" title="' + post.title + '">' + post.title + '</a>' +
dateHtml +
'</div>' +
'</div>';
}

// 把拼接好的HTML插入显示容器
displayContainer.innerHTML = htmlContent;
};

这段代码主要作用,就是随机生成文章,显示在页面中,通过 JS 调用,刷新页面重建数据。

然后打开同文件夹下的 index.pugcard_random_post.pug 文件引入到模板中,在这个文件夹中,找到如下代码

1
2
3
4
5
else
//- page
!=partial('includes/widget/card_author', {}, {cache: true})
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_weixin', {}, {cache: true})

你如果修改过这个文件,应该清除把 card_random_post.pug 放到哪里,我把这个 card_random_post.pug 文件放在了

1
!=partial('includes/widget/card_weixin', {}, {cache: true})

代码下面,我的代码如下

1
2
3
4
5
6
7
else
//- page
!=partial('includes/widget/card_author', {}, {cache: true})
!=partial('includes/widget/card_weixin', {}, {cache: true})
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_recent_post', {}, {cache: true})
!=partial('includes/widget/card_random_post', {}, {cache: true})

到这里代码文件修改完成,我顺便调用了 最近发布 板块。

在这里顺便记录一下安知鱼主题的 iconfont 库的链接地址,因为需要修改这个板块的图标,所以查阅了一下

1
https://www.iconfont.cn/collections/detail?cid=44481

使用起来会很方便的。

4.补充精简代码

补充的这份代码,没有 JS 调用,仅在 Hexo 编译时生成一次随机列表,刷新网页不会变化。

核心原因是 随机逻辑在服务端编译阶段执行 ,而且只是仅执行了一次,而非在浏览器端每次刷新时执行。分享给大家只是为了做一个标记

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
//- 侧边栏随机文章卡片(沿用最新文章样式)
if theme.aside.card_random_post.enable && site.posts.length > 0
.card-widget.card-recent-post
.item-headline
i.anzhiyufont.anzhiyu-icon-dice-d20
span= _p('aside.card_random_post')

.aside-list
//- 核心:Fisher-Yates 洗牌算法打乱文章列表
-
// 将Hexo的posts集合转为普通数组
let postsArray = site.posts.toArray();
// 随机打乱数组(保证随机性均匀)
for (let i = postsArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[postsArray[i], postsArray[j]] = [postsArray[j], postsArray[i]];
}
// 读取配置的显示数量(和原组件逻辑一致)
let postLimit = theme.aside.card_random_post.limit === 0 ? postsArray.length : theme.aside.card_random_post.limit || 5;
// 截取指定数量的随机文章
let randomPosts = postsArray.slice(0, postLimit);

//- 渲染随机文章列表(完全复用原有样式结构)
- randomPosts.forEach(function(article){
- let link = article.link || article.path
- let title = article.title || _p('no_title')
// 复用封面图显示逻辑(是否显示封面、无封面样式)
- let no_cover = article.cover === false || !theme.cover.aside_enable ? 'no-cover' : ''
- let post_cover = article.cover
.aside-list-item(class=no_cover)
// 封面图区域
if post_cover && theme.cover.aside_enable
a.thumbnail(href=url_for(link) title=title)
img(src=url_for(post_cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title)
// 标题和日期区域
.content
a.title(href=url_for(link) title=title)= title
// 日期显示逻辑(支持按创建时间/更新时间显示)
if theme.aside.card_random_post.sort === 'updated'
time(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated)) #[=date(article.updated, config.date_format)]
else
time(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date)) #[=date(article.date, config.date_format)]
- })

仅做记录,这份代码不如第一份代码调用数据灵活。

5.检查效果生成

最后 Hexo 三连(hexo cl && hexo g && hexo s)就可以看到文章中第二张图调用的效果。