摘要生成中...
AI 摘要
Hunyuan-lite

一份古老的代码传承至今,已经有很多的版本…这里介绍我的版本。

随机访问文章的实现思路

随机文章跳转的基本原理就三步:

  • 提前准备好一张站点列表
  • 读取列表,并随机挑一个出来
  • 访问目标地址

站点列表的准备

站点列表可以自己准备,或者使用插件 hexo-generator-sitemap 来生成。

站点列表的样子:

1
2
3
4
5
6
https://blog.uuanqin.top/p/18bbd410/
https://blog.uuanqin.top/p/c7d4d021/
https://blog.uuanqin.top/pages/copyright/index.html
https://blog.uuanqin.top/p/51890a9/
https://blog.uuanqin.top/p/91b7dad/
https://blog.uuanqin.top/pages/about/index.html

优化点

为了保证函数的精简和高效,本文代码有以下优化点:

  • 不重复请求 sitemap.txt,而是利用浏览器本地缓存(带过期)加速网站列表的获取
  • 随机跳转有不依赖任何框架的 CSS 简单提示
  • 随机跳转时将保持 url 参数,如想移除,可自行在REMOVED_PARAMS中填入。

Hexo-Butterfly 主题的使用

这里以菜单栏按钮为例。_config.butterfly.yml 中配置如下:

1
2
3
4
5
6
7
8
# Menu 目錄
menu:
随机: javascript:randomPost(); || iconfont icon-suijibofang

inject:
head:
# 随机页面
- <script src="/js/random.js"></script>

random.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
/**
* 随机文章跳转插件
*/
const RANDOM_CONFIG = {
SITEMAP_URL: '/sitemap.txt',
PATH_FILTER: '/p/',
CACHE_KEY: 'random_post_data',
CACHE_TIME: 24 * 60 * 60 * 1000, // 24小时
FALLBACK_URL: '/', // 找不到文章时的兜底地址
REMOVED_PARAMS: [] // 需要过滤的敏感参数。示例:['code', 'state']
};

async function randomPost() {
// --- 1. 辅助函数:用户友好提示 (Snackbar) ---
const showNotice = (msg) => {
let snack = document.getElementById('random-snack');
if (!snack) {
snack = document.createElement('div');
snack.id = 'random-snack';
// 极简内联样式:深色圆角、居中浮动、淡入淡出
Object.assign(snack.style, {
position: 'fixed', bottom: '30px', left: '50%', transform: 'translateX(-50%)',
backgroundColor: '#333', color: '#fff', padding: '10px 20px', borderRadius: '5px',
zIndex: '9999', fontSize: '14px', transition: 'opacity 0.3s'
});
document.body.appendChild(snack);
}
snack.innerText = msg;
snack.style.opacity = '1';
setTimeout(() => snack.style.opacity = '0', 2000);
};

try {
let urls = [];
const cached = localStorage.getItem(RANDOM_CONFIG.CACHE_KEY);
const now = Date.now();

// --- 2. 获取数据 (缓存优先) ---
if (cached) {
const { data, timestamp } = JSON.parse(cached);
if (now - timestamp < RANDOM_CONFIG.CACHE_TIME) urls = data;
}

if (urls.length === 0) {
showNotice('正在同步文章列表…');
const res = await fetch(RANDOM_CONFIG.SITEMAP_URL);
if (!res.ok) throw new Error();
const text = await res.text();

urls = text.split('\n')
.map(l => l.trim())
.filter(l => l !== '')
.map(u => {
try {
let p = new URL(u).pathname;
return p.endsWith('/') ? p : p + '/';
} catch(e) { return null; }
})
.filter(p => p && p.includes(RANDOM_CONFIG.PATH_FILTER));

localStorage.setItem(RANDOM_CONFIG.CACHE_KEY, JSON.stringify({ data: urls, timestamp: now }));
}

// --- 3. 随机筛选 ---
const current = window.location.pathname.endsWith('/') ? window.location.pathname : window.location.pathname + '/';
const candidates = urls.filter(p => p !== current);

if (candidates.length === 0) {
showNotice('没有发现其他文章,正在返回主页…');
setTimeout(() => location.href = RANDOM_CONFIG.FALLBACK_URL, 1500);
return;
}

const targetPath = candidates[Math.floor(Math.random() * candidates.length)];

// --- 4. URL 净化与跳转 ---
const params = new URLSearchParams(window.location.search);
RANDOM_CONFIG.REMOVED_PARAMS.forEach(p => params.delete(p));

const search = params.toString() ? '?' + params.toString() : '';
const finalUrl = targetPath + search + window.location.hash;

showNotice('正在穿越到随机文章…');
setTimeout(() => location.href = finalUrl, 800); // 留出一点时间让用户看到提示

} catch (err) {
console.error('RandomPost Error:', err);
showNotice('跳转失败,正在返回主页…');
setTimeout(() => location.href = RANDOM_CONFIG.FALLBACK_URL, 1500);
}
}

本文参考