一份古老的代码传承至今,已经有很多的版本…这里介绍我的版本。
随机访问文章的实现思路
随机文章跳转的基本原理就三步:
提前准备好一张站点列表
读取列表,并随机挑一个出来
访问目标地址
站点列表的准备
站点列表可以自己准备,或者使用插件 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: 随机: 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 , FALLBACK_URL : '/' , REMOVED_PARAMS : [] }; async function randomPost ( ) { 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 (); 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 })); } 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 )]; 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 ); } }
本文参考