Hexo 博客折腾记:从零部署 Waline 到完美复刻 Argon 极客页脚
前言:
生命不息,折腾不止。为了给博客加上评论功能,我选择了 Waline。本以为是“一键部署”,结果踩了无数 Vercel 配置与 NPM 版本的坑。
搞定评论后,又顺手把 Butterfly 主题的页脚魔改成了 Argon 风格(显示服务器 IP、访客信息、精准运行时间)。
这是一篇保姆级避坑指南,记录了从后端搭建到前端 UI 深度魔改的全过程。
🛠 第一阶段:Waline 评论系统 —— 后端基石
Waline 的运行依赖于:LeanCloud (存储) + GitHub (托管) + Vercel (算力)。
1. 注册数据库 (LeanCloud)
- 访问 LeanCloud 官网(推荐使用国际版,免备案且稳定)。
- 创建应用:点击“创建应用”,建议起名为
blog-comment。
- 获取凭证:进入
应用 -> 设置 -> 应用凭证。
请务必记下: App ID、App Key 和 Master Key。这是后续 Vercel 连接数据库的“钥匙”。
2. 构建代码仓库 (GitHub)
我们不使用一键部署,而是手动创建最纯净的代码结构,这能有效避免 Vercel 的路径识别报错。
- 在 GitHub 新建仓库
waline-server。
- 手动创建并提交以下三个核心文件:
1 2 3 4
| { "scripts": { "start": "waline" }, "dependencies": { "@waline/vercel": "latest" } }
|
api/index.js:Serverless 函数入口。
⚠️ 避坑准则:入口文件必须放在 api 文件夹内,否则 Vercel 无法将其识别为后端服务。
1 2
| const { Waline } = require('@waline/vercel'); module.exports = Waline({ env: 'vercel' });
|
vercel.json:重定向逻辑。
⚠️ 避坑准则:没有它,访问域名会报 404 或直接暴露源码。
1 2 3 4
| { "version": 2, "rewrites": [{ "source": "/(.*)", "destination": "/api/index.js" }] }
|
3. 服务上线 (Vercel)
- 使用 GitHub 账号 登录 Vercel。
- Import Project:导入刚才创建的
waline-server。
- 配置环境变量:在
Environment Variables 中填入:
LEAN_ID / LEAN_KEY / LEAN_MASTER_KEY
- 点击 Deploy。完成后你将获得一个
serverURL 地址,建议绑定自己的子域名。
🎨 第二阶段:Butterfly 主题原生接入
服务端搞定后,回到 Hexo 修改 _config.butterfly.yml:
1 2 3 4 5 6 7 8 9 10 11 12
| comments: use: Waline count: true
waline: serverURL: https://your-waline-url.vercel.app pageview: true meta: ['nick', 'mail'] requiredMeta: ['nick', 'mail']
|
🚀 第三阶段:复刻 Argon 硬核页脚 (深度魔改)
Butterfly 原生页脚略显单调,魔改版将实现:透明背景、全员居中、实时访客/服务器 IP 解析。
1. 结构重组:修改 Pug 模版
修改 themes/butterfly/layout/includes/footer.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
| //- 使用 Flex 布局实现绝对居中,背景透明适配各种主题色 #footer-wrap(style="background: transparent; color: #fff; text-align: center; padding: 20px 10px; display: flex; flex-direction: column; align-items: center; justify-content: center;") .argon-footer(style="font-family: -apple-system, sans-serif; font-size: 14px; line-height: 1.8; width: 100%;") //- 行1:服务器位置与运营商信息 #server-info(style="margin: 5px 0;") | 现在为您提供服务的服务器是 span#cf-country 检测中... | img#cf-flag(src="https://raw.githubusercontent.com/hampusborgos/country-flags/main/svg/us.svg", alt="国旗", style="width: 16px; height: 16px; display:none; vertical-align: text-bottom; margin: 0 4px;") | 城市: span#cf-city ... | ,IP: span#cf-ip ... | ,运营商: span#cf-provider Hugging Face
//- 行2:访客地理位置解析 #user-info(style="margin: 5px 0;") | 您来自 span#user-country 检测中... | img#user-flag(src="", alt="国旗", style="width: 16px; height: 16px; display:none; vertical-align: text-bottom; margin: 0 4px;") | 城市: span#user-city ... | ,IP: span#user-ip ... | ,运营商: span#user-org ...
//- 行3:精准稳定运行时间 .time-container(style="margin: 5px 0;") | 本博客已稳定运行 span#timeid1.time-element(style="color: #ffd700; font-weight: bold; margin: 0 3px;") 0 | 天 span#timeid2.time-element(style="color: #ffd700; font-weight: bold; margin: 0 3px;") 0 | 小时 span#timeid3.time-element(style="color: #ffd700; font-weight: bold; margin: 0 3px;") 0 | 分 span#timeid4.time-element(style="color: #ffd700; font-weight: bold; margin: 0 3px;") 0 | 秒
//- 行4:致敬 Butterfly div(style="margin-top: 5px;") | Theme a(href="https://butterfly.js.org/", target="_blank", style="font-weight:bold; color: #fff; text-decoration: none; border-bottom: 1px dashed rgba(255,255,255,0.5);") Butterfly
|
2. 注入灵魂:增强版 JS 逻辑
新建或修改 source/js/custom.js。
技术亮点:
- 阿里云 DoH:替换被墙的 Google DNS,确保国内直连访客数据不报错。
- IP 数字净化:针对 CloudFront 等 CDN 返回的别名网址,正则提取真实数字 IP。
- PJAX 适配:监听
pjax:complete,确保切换页面时页脚数据不消失。
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
| function runArgonWidget() { const boomDate = new Date("2026/01/01 00:00:00"); function updateTime() { try { const now = new Date(); const diff = now - boomDate; if (diff < 0) return; document.getElementById("timeid1").innerText = Math.floor(diff / 86400000); document.getElementById("timeid2").innerText = Math.floor((diff % 86400000) / 3600000); document.getElementById("timeid3").innerText = Math.floor((diff % 3600000) / 60000); document.getElementById("timeid4").innerText = Math.floor((diff % 60000) / 1000); } catch (err) {} } if (window.argonInterval) clearInterval(window.argonInterval); window.argonInterval = setInterval(updateTime, 1000); updateTime();
const userCountry = document.getElementById('user-country'); if (userCountry) { userCountry.innerText = "查询中..."; fetch('https://ipapi.co/json/') .then(res => res.json()) .then(data => { userCountry.innerText = data.country_name || "未知"; document.getElementById('user-city').innerText = data.city || "..."; document.getElementById('user-ip').innerText = data.ip || "..."; document.getElementById('user-org').innerText = data.org || "运营商"; const flag = document.getElementById('user-flag'); if (data.country_code) { flag.src = `https://raw.githubusercontent.com/hampusborgos/country-flags/main/svg/${data.country_code.toLowerCase()}.svg`; flag.style.display = 'inline'; } }).catch(() => { userCountry.innerText = "网络异常"; }); }
const cfCountry = document.getElementById('cf-country'); if (cfCountry && window.location.hostname !== 'localhost') { cfCountry.innerText = "解析中..."; fetch(`https://dns.alidns.com/resolve?name=${window.location.hostname}&type=1`) .then(res => res.json()) .then(data => { if (data.Answer) { const ipRecord = data.Answer.find(ans => ans.type === 1); document.getElementById('cf-ip').innerText = ipRecord ? ipRecord.data : data.Answer[0].data; cfCountry.innerText = "Global CDN"; document.getElementById('cf-flag').style.display = 'inline'; } }); } }
runArgonWidget();
document.addEventListener('pjax:complete', runArgonWidget);
|
第四阶段:激活脚本 —— 引入 custom.js
代码写完并不代表它会自动运行。Butterfly 主题需要通过配置文件手动“点火”,才能在浏览器中加载并执行你的自定义逻辑。
1. 修改主题配置文件
打开博客根目录下的 _config.butterfly.yml,搜索 inject 配置项。
2. 添加引用代码
在 bottom(即页面底部,</body> 标签之前)添加你的脚本路径。请确保这里的路径与你在 source 文件夹下存放的文件路径一致。
1 2 3 4 5 6 7
| inject: head: bottom: - <script src="/js/custom.js"></script>
|
3. 强制刷新与生效
⚠️ 避坑准则:Hexo 的缓存非常顽固,修改完 JS 后必须执行“三连”操作,否则浏览器可能还在运行旧的代码。
在终端执行:
1
| hexo clean && hexo g && hexo s
|
然后在浏览器中按下 Ctrl + F5(Windows)或 Cmd + Shift + R(Mac)进行强制刷新。
📝 避坑终极避坑总结
- 缓存是大敌:修改完配置务必执行
hexo clean,否则你看到的永远是旧样式。
- DNS 策略:国内环境下必须使用阿里云/腾讯云的 DNS 接口,使用 Google DNS 会导致直连访客看到“DNS 错误”。
- 玄学位置:若开启了 iCloud 专用代理 或 梯子,访客 IP 会显示为美国/日本,这是物理层面的代理转发,属正常现象。
结语:Hexo 的魅力就在于折腾。当看到页脚的跳动秒数与精准的服务器信息交织在一起时,这一天的汗水都化成了满满的成就感。
Would you like me to help you further with specific CSS styling for the footer elements?